DRF - Filters in ModelSerializer - django

In my models an User is related with its Profile, which has Companies. In order to serialize them I want that if the user who makes the request is_staff, then the serializer must return all the companies, not only the ones s/he has through the relationship in the model.
What's the proper way to...:
1) Check if the user is staff.
2) Return all the companies if user is staff or return the companies of the profile related with the user.
I guess the best way to deal with this would be check in BProfileSerializerRelated if the user is staff and then add something like this:
company = serializers.SerializerMethodField('get_companies')
def get_companies(self, obj):
companies = Company.objects.all()
serializer = CompanySerializer(instance=companies, many=True)
return serializer.data
My current code doesn't take into account if the user is staff so return just the companies related with that user:
models.py
class BUser(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=40, unique=True)
first_name = models.CharField(max_length=40)
last_name = models.CharField(max_length=40)
class BProfile(models.Model):
user = models.ForeignKey('BUser')
company = models.ForeignKey('BCompany')
groups = models.ManyToManyField(Group)
class BCompany(models.Model):
name = models.CharField(max_length=64)
dealer = models.ForeignKey(BProfile, related_name='companies', blank=True, null=True)
views.py
#api_view(['GET'])
def current_user_detail(request):
serializer = BUserSerializerRelated(request.user)
return Response(serializer.data)
serializers.py
class BUserSerializerRelated(serializers.ModelSerializer):
bprofile_set = BProfileSerializerRelated(many=True)
class Meta:
model = BUser
fields = ('id', 'bprofile_set', 'username', 'email', 'first_name', 'last_name')
class BProfileSerializerRelated(serializers.ModelSerializer):
company = CompanySerializer()
groups = GroupSerializer(many=True)
dealer_companies = CompanySerializer(many=True)
class Meta:
model = BProfile
fields = ('id', 'dealer_companies', 'company', 'groups')
class CompanySerializer(serializers.ModelSerializer):
sites = SiteSerializer(many=True)
services = ServiceSerializer(many=True)
class Meta:
model = Company
fields = ('id', 'dealer','name', 'cif', 'sites', 'services')

Serializer has request object in his context dict. So you can get the current user from there.
def get_companies(self, obj):
user = self.context['request'].user
if user.is_staff:
serializer = CompanySerializer(Company.objects.all(), many=True)
else:
serializer = CompanySerializer(instance=companies, many=True)
return serializer.data

To access request object in serializer you need to pass it context dict while initializing serializer i.e in your views.py
serializer = BUserSerializerRelated(request.user, context={'request': self.request})
then in your serializers.py, you can access request user object like
user = self.context['request'].user

Related

DRF SERILAZATION

I serialize the field named "product" with ProductSerializer() inside OrderItemSerializer().
That's what I want.
class OrderItemSerializer(serializers.ModelSerializer):
product = ProductSerializer()
class Meta:
model = models.OrderItem
fields = ('id','order', 'product', 'quantity')
The output is;
But when I try to request with POST Method needs to send Product as a dictionary, just giving the id value is not enough.
How can I POST by sending only the id value?
I haven't written anything about the operation yet. Default ModelViewSet
class OrderItemViewSet(ModelViewSet):
queryset = OrderItem.objects.all()
serializer_class = serializers.OrderItemSerializer
permission_classes = (IsOwnerOrNot, IsAuthenticated)
def get_queryset(self):
user = self.request.user
return self.filter_queryset(queryset=self.queryset.filter(order__user=self.request.user))
If you're supporting writable nested representations you'll need to write .create() or .update() methods that handle saving multiple objects.
class UserSerializer(serializers.ModelSerializer):
profile = ProfileSerializer()
class Meta:
model = User
fields = ['username', 'email', 'profile']
def create(self, validated_data):
profile_data = validated_data.pop('profile')
user = User.objects.create(**validated_data)
Profile.objects.create(user=user, **profile_data)
return user

Django Rest Framework serializer check if exists in related Many to Many fiield

I have a custom User model with a blocked field as
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
blocked = models.ManyToManyField("User", related_name="blocked_by", blank=True)
and I'm trying to have a serializer where I can look up a user by their ID and find out if they are blocked or not by the logged in user
my current is serializer
class UserSerializer(serializers.ModelSerializer):
is_blocked = serializers.BooleanField() # need to populate this
class Meta:
model = User
fields = ['id', 'email', 'user_type', 'is_blocked']
and my view
class UserDetail(generics.RetrieveAPIView):
authentication_classes = (authentication.JWTAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
serializer_class = UserSerializer
queryset = User.objects.all()
I'm just not sure how to populate that value such that it'd be true if the inquiried user is in the blocked manyToMany field of the logged in user or not
You can use SerializerMethodField and calculate it. Get the authenticated user and check if the serialized user is in the blocked list.
class UserSerializer(serializers.ModelSerializer):
is_blocked = serializers.SerializerMethodField()
class Meta:
model = User
fields = ['id', 'email', 'user_type', 'is_blocked']
def get_is_blocked(self, obj):
# you might need to do some fixes in this method, I'm not 100% sure that it works like you want
logged_in_user = self.context['request'].user # the authenticated user
is_blocked = logged_in_user.blocked.filter(id=obj.id).exists() # check if the serialized user (obj) is in the auth user blocked list
return is_blocked

Querying and Filtering related models in DRF

I have Contact model to list the followers of an User object, I try to filter the contacts of a User but I still could not manage get a correct queryset. My Contact model is simple with two ForeignKey:
class Contact(models.Model):
user_from = models.ForeignKey(User,related_name='rel_from_set', on_delete=models.CASCADE,)
user_to = models.ForeignKey(User,related_name='rel_to_set', on_delete=models.CASCADE,)
def __str__(self):
return '{} follow {}'.format(self.user_from, self.user_to)
I have created serializers for User and Contact:
##Contact Serializer
class ContactsSerializer(serializers.ModelSerializer):
user_from = serializers.StringRelatedField(read_only=True)
user_to = serializers.StringRelatedField(read_only=True)
class Meta:
model = Contact
fields = ["user_from", "user_to"]
##UserSerializer
class UserInformationSerializer(serializers.ModelSerializer):
followers = ContactsSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['first_name', 'last_name', 'followers']
​
And try to make a query through views:
class FollowerListView(APIView):
queryset = Contact.objects.all()
serializer_class = ContactsSerializer
lookup_field = "username"
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).filter(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
Now I get error message:
AttributeError at /api/member/ytsejam/followers/
'QuerySet' object has no attribute 'username'
print(user.username)
If i try print(user) I can see the user an Object.
Can you guide me how to correct?
Thanks
filter will always return a queryset. If you expect to retrieve one single item, use get.
So that it looks like that:
def get(self, request, format=None, slug=None):
kwarg_username = self.kwargs.get("slug")
user = User.objects.filter(is_active=1).get(username=kwarg_username)
print(user.username)
contacts = Contact.objects.filter(user_to=user.id)
serializer = ContactsSerializer(contacts)
return Response(serializer.data)
You could, of course, do this on one take:
User.objects.get(is_active=1, username=kwarg_username)
But beware, if there are two rows in your model that would satisfy this call, Django will throw an error. Best make sure that the username has a unique constraint.

How to fix 'Error validation unque field by serializer when update instance'

I have a model of Organisation and three models have Foreign keys to Organisation model. Three nested models is Users ( custom model ), Description and Contacts. Users has unique field email. Description has unique pair of two fields. I have custom serializer to Organisation.
class OrganisationSuperAdminSerializer(serializers.ModelSerializer):
users = UsersSerializer(many=True, required=False)
contacts = ContactsSerializer(many=True, required=False)
description = DescriptionOrganisationSerializer(many=False, required=False)
class Meta:
model = Organisation
fields = '__all__'
def create(self, validated_data):
error_msg = 'Save error'
users_data = validated_data.pop('users')
contacts_data = validated_data.pop('contacts')
description_data = validated_data.pop('description')
organisation = Organisation.objects.create(**validated_data)
try:
for user_data in users_data:
Users.objects.create(organisation=organisation, **user_data)
for contact_data in contacts_data:
Contacts.objects.create(organisation=organisation, **contact_data)
DescriptionOrganisation.objects.create(organisation=organisation, **description_data)
except:
organisation.delete()
raise serializers.ValidationError(error_msg)
return {}
def update(self, instance, validated_data):
pass
When I save, everything goes well. But when I try to update, the serializer fails validation. The error text in the comments.
"""
Класс для работы с данными для супер админа
"""
queryset = Organisation.objects.all()
serializer_class = OrganisationSuperAdminSerializer
permission_classes = [permissions.AllowAny, ]
def update(self, request, pk=None, *args, **kwargs):
serializer: serializers.ModelSerializer = self.get_serializer(self.get_object(), data=request.data)
print(serializer.is_valid()) # False
print(serializer.errors) # {'users': [{'email': [ErrorDetail(string='email must be unique', code='unique')]}], 'description': {'non_field_errors': [ErrorDetail(string='The fields inn, kpp must make a unique set.', code='unique')]}}
return response.Response(status=200)
I don't want to disable validation of unique fields. But I can't find information how to validate through the serializer update.
Other serializers:
class UsersSerializer(serializers.ModelSerializer):
email = serializers.CharField(max_length=128,
validators=[validators.UniqueValidator(
queryset=Users.objects.all(),
message='email must be unique'
)]
)
class Meta:
model = Users
fields = '__all__'
class DescriptionOrganisationSerializer(serializers.ModelSerializer):
organisation = serializers.PrimaryKeyRelatedField(required=False, queryset=DescriptionOrganisation.objects.all())
class Meta:
model = DescriptionOrganisation
fields = '__all__'
class ContactsSerializer(serializers.ModelSerializer):
organisation = serializers.PrimaryKeyRelatedField(required=False, queryset=Contacts.objects.all())
class Meta:
model = Contacts
fields = '__all__'

How to save a modelSerializer that has relations? - django

I want to save a sent json data to db by django-rest-framework.
the problem is, not saving the relation and returns error.
The bellow snippet is my models:
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, related_name='profile', on_delete=models.CASCADE)
name = models.CharField(max_length=30)
family = models.CharField(max_length=50)
class Klass(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=500)
teacher = models.ForeignKey(Profile, related_name='teacher', on_delete=models.CASCADE)
I use below serializer for serializing/deserializing the Klass model.
class ProfileSerializer(serializers.ModelSerializer):
class Meta:
model = Profile
fields = ('pk', 'name', 'family')
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
now when I prepare a JSON object and send it to the view, it returns error. the below is the view class:
class KlassView(APIView):
"""for SELECT, INSERT Queries"""
def get(self, request, pk):
# somthing
#csrf_exempt
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save()
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
and the error is:
The .create() method does not support writable nested fields by default.
Write an explicit .create() method for serializer mainp.serializers.KlassSerializer, or set read_only=True on nested serializer fields.
How can I save relation in KlassSerializer in order to save to db?
At first change your serializer like below:
class KlassSerializer(serializers.ModelSerializer):
# teacher = ProfileSerializer() # No need to this!
class Meta:
model = Klass
# fields = ('id', 'title', 'description', 'teacher')
fields = ('id', 'title', 'description') # Omit teacher
Then get profile from requested user and pass it to your serializer:
def post(self,request, pk=None):
"""For Creating A Class"""
serializer = KlassSerializer(data=request.data)
if serializer.is_valid():
teacher = ProfileSerializer(request.data['teacher']['pk'])
serializer.teacher = teacher.data
serializer.save(teacher=request.user.profile) # Retrieve teacher and stroe
return Response({'data': serializer.data})
else:
return Response({'data': serializer.errors})
Just override the create method of ModelSerializer in KlassSerializer.
class KlassSerializer(serializers.ModelSerializer):
teacher = ProfileSerializer()
class Meta:
model = Klass
fields = ('id', 'title', 'description', 'teacher')
def create(self, validated_data):
profile = Profile.objects.filter(pk=validated_data['teacher']['pk'])
if profile:
k = Klass()
k.teacher = profile
...