Django Rest Framework foreign key multiple data - django

I have a model call Schedule, it is like reminder function. I also have another model called Family which user can add family member in it.
So in schedule, i create a foreign key to link family inside so that it will include the family member id in it.
Here is the how my API for schedule looks like: https://imgur.com/a/bwYDn
And here are my questions.
As you can see based on the image in above link, the userId is a drop down. is it possible to make it userId = self.request.user.userId ?
In the schedule api, the familyId is a drop down that consist of all family member(basically, even other people who added their family), is there a way to filter it so that it will only shows a dropdown of only the current user familyId appear ?
When creating a schedule, user can only insert 1 familyId. Is there a way to choose more than 1 familyId ? For eg, user can insert familyId 1 and 2
Here is my code
models.py
class MyUser(AbstractUser):
userId = models.AutoField(primary_key=True)
gender = models.CharField(max_length=6, blank=True, null=True)
nric = models.CharField(max_length=40, blank=True, null=True)
birthday = models.DateField(blank=True, null=True)
birthTime = models.TimeField(blank=True, null=True)
class Family(models.Model):
userId = models.ForeignKey(MyUser)
relationship = models.CharField(max_length=100)
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
gender = models.CharField(max_length=6, blank=True, null=True)
nric = models.CharField(max_length=40, blank=True, null=True)
class Schedule2(models.Model):
userId = models.ForeignKey(MyUser, on_delete=models.CASCADE)
familyId = models.ForeignKey(Family, on_delete=models.CASCADE)
date = models.DateField()
time = models.TimeField()
location = models.CharField(max_length=255, blank=True, null=True)
title = models.Charfield(max_length=100, blank=True, null=True)
desc = models.CharField(max_length=300, blank=True, null=True)
serializer.py
class FamilySerializer(serializers.ModelSerializer):
class Meta:
model = Family
fields = ('id', 'userId', 'first_name', 'last_name', 'gender', 'nric', 'relationship')
class Schedule2Serializer(serializers.ModelSerializer):
valid_time_formats = ['%H:%M', '%I:%M%p', '%I:%M %p']
time = serializers.TimeField(format='%I:%M %p', input_formats=valid_time_formats)
class Meta:
model = Schedule2
fields = ('id', 'userId', 'familyId', 'title', 'desc', 'date', 'time', 'location')
views
class FamilyViewSet(viewsets.ModelViewSet):
permission_classes = [AllowAny]
queryset = Family.objects.all()
serializer_class = FamilySerializer
# this is to allow current user to see their own family only
def get_queryset(self):
user = self.request.user
return Family.objects.filter(userId=user)
class ScheduleViewSet2(viewsets.ModelViewSet):
permission_classes = [AllowAny]
queryset = Schedule2.objects.all()
serializer_class = Schedule2Serializer

The answer for the first question is "yes", you can set the user_id in the model to the id of the person logged in (self.request.user). To do this you will have to overwrite the create method.

Related

Django serializer data

I need to get value of basket in 'title' not in 'id'.
How can I do this? How can I get a value of 'title' from 'Position' model in another 'Client' model using ManyToManyField. It automatically transmits ID and the 'title' is required
I have tried many ways but... It must be easy, but i search info 2 days
class Position(models.Model):
title = models.CharField(max_length=150, verbose_name='Title')
slug = models.SlugField(max_length=100, unique=True, db_index=True, verbose_name='URL')
description = models.CharField(max_length=500, verbose_name='Describe')
photo = models.ImageField(upload_to="photos/%Y/%m/", verbose_name='Photo', null=True)
price = models.DecimalField(decimal_places=2, max_digits=10, verbose_name='Price')
date_create = models.DateTimeField(auto_now_add=True, verbose_name='Date create')
date_update = models.DateTimeField(auto_now=True, verbose_name='Date update')
is_published = models.BooleanField(default=True, verbose_name='Is published')
in_stock = models.BooleanField(default=True, verbose_name='In stock')
class Client(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
basket = models.ManyToManyField('Position', default=None, blank=True, related_name='basket')
def __str__(self):
return f'{self.user.username}, id-{self.user.id}'
class ClientSerializer(serializers.ModelSerializer):
class Meta:
model = Client
fields = "__all__"
class ClientViewSet(viewsets.ModelViewSet):
serializer_class = ClientSerializer
permission_classes = (IsOwnerOrReadOnly,)
def get_queryset(self):
pk = self.kwargs.get('pk')
# need a list of objects, not an one
return Client.objects.filter(pk=pk)
result:
{
"id": 1,
"user": 1,
"basket": [
1
]
}
need something like this - "basket":['monitor','keyboard']
You can use the PositionSerializer to get the title of each Position object in the Client's basket ManyToManyField.
class PositionSerializer(serializers.ModelSerializer):
class Meta:
model = Position
fields = ('title',)
class ClientSerializer(serializers.ModelSerializer):
basket = PositionSerializer(many=True, read_only=True)
class Meta:
model = Client
fields = "__all__"

How to inner join multi tables or merge multi serializers in Django?

i have this code here for the models:
class Users(models.Model):
first_name = models.CharField(max_length=32, blank=True, null=True)
last_name = models.CharField(max_length=32, blank=True, null=True)
email = models.EmailField(max_length=254, unique=True)
class Images(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
encoded_pic = models.JSONField(
encoder=None, default=dict, blank=True, null=True)
pic_thumbnail_path = models.CharField(max_length=222, blank=True, null=True)
class WorkingDays(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
day = models.DateField(blank=True, null=True)
enter_time = models.DateTimeField(blank=True, null=True)
exit_time = models.CharField(max_length=32, blank=True, null=True)
class Departments(models.Model):
user= models.ForeignKey(Users, on_delete=models.RESTRICT)
department_name = models.CharField(max_length=32, blank=True, null=True)
These are the serializers:
class UserssSerializer(serializers.ModelSerializer):
class Meta:
model = Users
fields = '__all__'
class ImagesSerializer(serializers.ModelSerializer):
class Meta:
model = Images
fields = '__all__'
class WorkingDaysSerializer(serializers.ModelSerializer):
class Meta:
model = WorkingDays
fields = '__all__'
class DepartmentsSerializer(serializers.ModelSerializer):
class Meta:
model = WorkingDays
fields = '__all__'
I tried this code but it only returns the images fields not the others
data=Images.objects.filter(user_id__workingdays=pk).values()
What i want to do is to inner join images, workingdays and departments table using user_id field
also is there away to merge serializers of all these 3 tables to make the serializer returns all the fields from all the 3 tables?

Django api filter search on other models without a direct foreign field

I have two models named user, skill, and profile.
I am trying to implement a search filter on the user's skills. which means when someone searches for something that is contained in the skills of a user, that user would appear in the search result.
Note: when the user signs up, a signal is used to auto-create a profile for that user. The user simply updates their profile to add skills and other things.
user model
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(max_length=254, unique=True)
name = models.CharField(max_length=250)
picture = models.TextField(null=True, blank=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
last_login = models.DateTimeField(null=True, blank=True)
date_joined = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(max_length=255, unique=True, blank=True)
profile model
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profiles')
date_of_birth = models.DateField(blank=True, verbose_name="DOB", null=True)
bio = models.TextField(max_length=500, blank=True, null=True)
skills = models.ManyToManyField(Skill, related_name='skills')
sex = models.CharField(max_length=6, choices=SEX, blank=True, null=True)
type_of_body = models.CharField(max_length=8, choices=BODYTYPE, blank=True, null=True)
feet = models.PositiveIntegerField(blank=True, null=True)
inches = models.PositiveIntegerField(blank=True, null=True)
lives_in = models.CharField(max_length=50, blank=True, null=True)
updated_on = models.DateTimeField(auto_now=True)
skill model
class Skill(models.Model):
name = models.CharField(max_length=60)
subcategory = models.CharField(max_length=60, blank=True, null=True)
description = models.TextField(null=True, blank=True)
created_on = models.DateTimeField(auto_now=True)
updated_on = models.DateTimeField(auto_now_add=True)
updated_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.DO_NOTHING)
the user view, where the search is done from
class ListUsersView(generics.ListAPIView):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
filter_backends = [filtr.SearchFilter]
search_fields = ['email', 'name']
currently, the solution above works, but when I add to the search_fields other fields like profiles__skills in order to include results where there is a skill like that created by ay user, the code doesn't work.
Please, how can I get the skills in the profile of a user to show in the search?
The SearchFilter class supports simple single query parameter based searching. The search_fields attribute should be a list of names of text type fields on the model.
profiles__skills is not a field. You should use a text field eg. profiles__skills__name
class ListUsersView(generics.ListAPIView):
'''
Gets all the users in the database
'''
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes = [AllowAny]
filter_backends = [filtr.SearchFilter]
search_fields = ['email', 'name', 'profiles__skills__name']

Django admin page autocomplete on reverse ForeignKey

Disclamer:
This is an edit question because I got some answers until now.
I would like to create an autocomplete field in InlineModelAdmin, with similar behavior as autocomplete_fields. I know, Django natively supports this only on ForeignKey, but I need it on the reverse side.
Is there a way - or a library that could do this?
I look into :
https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html
https://django-grappelli.readthedocs.io/en/latest/customization.html
https://django-admin-autocomplete-all.readthedocs.io/en/latest/readme.html
But I didn't found this functionality...
Past question code:
I have models like this:
Models
class Customer(models.Model):
customer_ref = models.CharField(unique=True, max_length=50)
name = models.CharField(null=False, max_length=80)
country = models.CharField(null=True, max_length=50)
class Assortment(models.Model):
name = models.CharField(null=False, max_length=50)
customers = models.ManyToManyField(Customer, related_name='assortments', blank=True)
products = models.ManyToManyField(Product, related_name='assortments', blank=True)
class Subsidiary(models.Model):
subsidiary_ref = models.CharField(unique=True, max_length=50)
name = models.CharField(null=False, max_length=80)
address = models.TextField(null=True)
city = models.CharField(null=True, max_length=50)
coordinates_x = models.DecimalField(null=True, decimal_places=2, max_digits=6)
coordinates_y = models.DecimalField(null=True, decimal_places=2, max_digits=6)
phone_number = models.CharField(null=True, max_length=50)
frequency = models.ForeignKey(Frequency, on_delete=models.SET_NULL, null=True)
channel = models.CharField(null=True, blank=True, max_length=50)
subchannel = models.CharField(null=True, blank=True, max_length=50)
user = models.ForeignKey(User, related_name='subsidiaries', on_delete=models.SET_NULL, null=True)
day_planned = models.BooleanField(default=False)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='subsidiaries')
class Asset(models.Model):
identification = models.CharField(unique=True, max_length=50)
serial = models.CharField(max_length=50)
name = models.CharField(null=True, max_length=50)
subsidiary = models.ForeignKey(Subsidiary, related_name='assets', null=True, blank=True, on_delete=models.DO_NOTHING)
Admin
#admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ['customer_ref', 'name', 'country']
list_filter = ['country']
autocomplete_fields = ['assortments']
#admin.register(Subsidiary)
class SubsidiaryAdmin(admin.ModelAdmin):
exclude = ['day_planned']
list_display = ['subsidiary_ref', 'customer', 'name', 'address', 'phone_number', 'frequency', 'user']
list_editable = ['frequency', 'user']
list_filter = ['frequency', 'user']
search_fields = ['subsidiary_ref', 'customer__name', 'name']
autocomplete_fields = ['assets']
CustomerAdmin is 'working' without error but field 'assortments' is not visible.
SubsidiaryAdmin throws error :
<class 'mobileapp.admin.SubsidiaryAdmin'>: (admin.E038) The value of 'autocomplete_fields[0]' must be a foreign key or a many-to-many field.
witch is weird because I don't see any difference from the first example.
How to fix this?
assortments is not visible in the list page as you have set list_display to
list_display = ['customer_ref', 'name', 'country']
Since assortments is a many-to-many relationship, you have to write a custom ModelAdmin to display them.
As mentioned in the django docs, autocomplete_fields works only for FK & M2M fields of that model. In the AssetAdmin, you can set autocomplete for Subsidiary field.
#admin.register(Asset)
class AssetAdmin(admin.ModelAdmin):
autocomplete_fields = ['subsidiary']

Django Rest Framework- foreign key throwing error

I am using django rest framework wherein the model has composite primary key, one of the them being a foreign key.
models/TestSuite.py
class TestSuite(models.Model):
team_name = models.ForeignKey('Team', on_delete=models.DO_NOTHING, db_column='team_name')
suite_name = models.CharField(max_length=100)
description = models.CharField(max_length=200, blank=True, null=True)
schedule = models.CharField(max_length=100, blank=True, null=True)
email_list_ok = models.CharField(max_length=200, blank=True, null=True)
email_list_fail = models.CharField(max_length=200, blank=True, null=True)
template_name = models.ForeignKey('EmailTemplates', on_delete=models.DO_NOTHING, db_column='template_name')
class Meta:
managed = False
db_table = 'test_suite'
unique_together = (('team_name', 'suite_name'),)
models/Team.py
class Team(models.Model):
team_name = models.CharField(primary_key=True, max_length=30)
description = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = False
db_table = 'team'
TestSuiteSerializer.py
class Meta:
model = models.TestSuite
fields = '__all__'
TestSuiteViewSet.py
class TestSuiteViewSet(viewsets.ModelViewSet):
queryset = models.TestSuite.objects.all()
serializer_class = serializers.TestSuiteSerializer
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data,
many=isinstance(request.data, list))
serializer.is_valid(raise_exception=True)
self.perform_create(serializer)
headers = self.get_success_headers(serializer.data)
return Response(serializer.data,
status=status.HTTP_201_CREATED, headers=headers)
Now when I do a post request, it throws below errors
When the post() has team_name already existing in team table
{
"team_name": [
"test suite with this team name already exists."
]
}
When the post() has team_name not existing in team table
Exception Type: ValueError
Exception Value:
Cannot assign "'dummy'": "TestSuite.team_name" must be a "Team" instance.
Can someone please help me here. I am assuming I am missing something.
The first argument to your foreign key fields should be the model itself, not a string of the model (eg. not 'Team', but Team - likewise for EmailTemplate)
class TestSuite(models.Model):
# Change this field's first argument from a string to the Team class
team_name = models.ForeignKey(Team, on_delete=models.DO_NOTHING, db_column='team_name')
suite_name = models.CharField(max_length=100)
description = models.CharField(max_length=200, blank=True, null=True)
schedule = models.CharField(max_length=100, blank=True, null=True)
email_list_ok = models.CharField(max_length=200, blank=True, null=True)
email_list_fail = models.CharField(max_length=200, blank=True, null=True)
# Change this field's first argument from a string to the EmailTemplates class
template_name = models.ForeignKey(EmailTemplates, on_delete=models.DO_NOTHING, db_column='template_name')
class Meta:
managed = False
db_table = 'test_suite'
unique_together = (('team_name', 'suite_name'),)