How to validate Nested Serializer in Django Rest Framework - django

I have ReviewRatings and ReviewImages models in which ReviewImages is ForeignKey to ReviewRatings so that an user can upload single or multiple images along with reviews.
I have overridden the create method for ReviewRatings in ReviewRatingSerializer and creates ReviewImages in it also. I Have ReviewImagesSerializer also. So My doubt is How do I validate the images am looping through before saving it.
Is there anyway to call ReviewImagesSerializer in create method of ReviewRatingSerializer
to validate the files?
#Serializer.py
class ReviewImagesSerializer(ModelSerializer):
class Meta:
model = ReviewImages
fields = ["images"]
class ReviewSerializer(ModelSerializer):
user = SerializerMethodField()
review_images = ReviewImagesSerializer(many=True, required=False)
class Meta:
model = ReviewRatings
fields = [
"user",
"rating",
"review",
"created_at",
"updated_at",
"review_images",
]
def get_user(self, obj):
return f"{obj.user.first_name} {obj.user.last_name}"
def create(self, validated_data):
review_images = self.context["images"]
reviews = ReviewRatings.objects.create(**validated_data)
for image in review_images:
ReviewImages.objects.create(review=reviews, images=image)
return reviews

Related

how to post manytomany field by value not by id in django rest framework

I have a tow classes , the relationship between them many to many field , i wont to post data in this way
{"first_name":"mohammad", "last_name":"alqudah", "motivate":[ "Stay Fit","Look Younger" ] } instead of this way
{"first_name":"mohammad", "last_name":"alqudah", "motivate":[ 1,2 ] }
my Serializers
class MotivateSerializers(serializers.ModelSerializer):
class Meta:
model = Motivate
fields = ['name']
class UserSerializers(serializers.ModelSerializer):
class Meta:
model = User
fields = ['lat','long','first_name','last_name','mobile_number','email','date_of_birth',
'gender','height','weight','relationship','number_of_household','number_of_pets','kind_of_pets','motivate']
my views.py
#api_view(['POST', ])
#permission_classes([IsAuthenticated])
def userdata(request):
user = request.user
serializer = UserSerializers(instance=user, data=request.data,many=False)
if serializer.is_valid():
serializer.save()
return Response("Data has been updated successfully")
else:
# print (serializer.errors)
return Response(serializer.errors)
You can use SlugRelatedField. Example:
class UserSerializers(serializers.ModelSerializer):
# New
motivate = serializers.SlugRelatedField(
slug_field="name",
queryset=Motivate.objects.all()
many=True
)
class Meta:
model = User
fields = [
"first_name", "last_name",
...
"motivate"
]

how to post a model with a foreignkey using django rest framework with only one field?

I'm working on an API, django rest framework for the backend and react on the frontend.
i only want to create a model and pass the foreignkey as a dropdown like in django rest framework
but with reactjs
i have many models the ones without foreignkeys are esly manipulated
but i have some models with foreignkeys that i can get but can't POST even with postman i get:
"name": [
"This field is required."
]
i have seen many posts that has same issues but didn't understant gow to implement it
here is a simple model with a foreign key
models.py
class SalleSport(models.Model):
name = models.CharField( max_length=50)
adresse = models.CharField( max_length=50)
class Planning(models.Model):
name = models.CharField(max_length=50)
salle_sport = models.ForeignKey(SalleSport, verbose_name="Salle de sport", on_delete=
models.CASCADE, null=True, blank=True)
serializers.py
class SalleSportSerialiser(serializers.ModelSerializer):
class Meta:
model = SalleSport
fields= '__all__'
as i want to create a Planning instance i tried many solution that didn't worked i'm gona put them as comments
class PlanningSerialiser(serializers.ModelSerializer):
# salle_sport = SalleSportSerialiser(read_only=False)
class Meta:
model = Planning
fields= '__all__'
# fields= ('id', 'name', 'salle_sport')
# def create(self, validated_data):
# salle_sport = validated_data.pop('salle_sport', None)
# if author_data:
# salle_sport = SalleSport.objects.get_or_create(**salle_sport)[0]
# print('salle de sport',salle_sport)
# validated_data['salle_sport'] = salle_sport
# return Planning.objects.create(**validated_data)
# def create(self, validated_data):
# return Planning.objects.create(**validated_data)
# def to_representation(self, instance):
# # response = super(PlanningSerialiser, self).to_representation(instance)
# self.fields["salle_sport"] = SalleSportSerialiser(read_only=True)
# return super(PlanningSerialiser, self).to_representation(instance)
# def get_salle_sport(self, obj):
# return SalleSportSerialiser(instance=obj.salle_sport).data
i also tried to implement it on the views.py by over writing the create()
class PlanningAPIView(generics.CreateAPIView):
queryset = Planning.objects.all()
serializer_class = PlanningSerialiser
# def create(self, request):
# salle_sport = get_object_or_404(SalleSport, name=request.data.get('salle_sport'))
# serializer = self.get_serializer(data= request.data)
# serializer.is_valid(raise_exception=True)
# serializer.save(salle_sport=salle_sport)
# headers = self.get_success_headers(serializer.data)
# return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
i have read the documentation about nested serializers but i didn't know how to get it work.
Try
class PlanningSerialiser(serializers.ModelSerializer):
salle_sport = SalleSportSerialiser(read_only=False)
class Meta:
model = Planning
fields= ('salle_sport',)
I found the solution, here it is i had to provide one serializer to write and the other to read to get all i want :
this serializer is to get the SalleSport by names and use it to read
class NameSalleSportSerialiser(serializers.ModelSerializer):
class Meta:
model = SalleSport
fields= ('name',)
class PlanningListSerialiser(serializers.ModelSerializer):
salle_sport = NameSalleSportSerialiser()
class Meta:
model = Planning
fields= ('name', 'salle_sport')
this is the serializer that i used to create the nested Model ( with Foreignkey )
class PlanningSerialiser(serializers.ModelSerializer):
salle_id = serializers.PrimaryKeyRelatedField(queryset= SalleSport.objects.all(), source='salle_sport.id')
class Meta:
model = Planning
fields= ('name', 'salle_id')
def create(self, validated_data):
plan = Planning.objects.create(salle_sport=validated_data['salle_sport']['id'], name=validated_data['name'])
return plan
and in React side i fetched made tow request one GET to retreive all SalleSport instance that displays the names but post the ID and like this the user sees the names.

How to display Data from two model without creating new model in Django?

I have two models with one common field (email). I using viewset.ModelViewset to list and create data in both the models.
My models.py:
class Schema(models.Model):
"""Database model for Schema """
name= models.TextField()
version = models.TextField()
email = models.EmailField(unique = True )
def __str__(self):
return self.email
class Status(models.Model):
""" Model For status """
email = models.EmailField(unique = True )
status = models.TextField()
def __str__(self):
return self.email
my view.py:
class StatusViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = models.Status.objects.all()
serializer_class = serializers.StatusSerializer
class SchemaViewSet(mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = models.Schema.objects.all()
serializer_class = serializers.SchemaSerializer
I want to overide List function of Status Viewset to display entire detail of that user.
For ex: from schema viewset i have created :
{
"id": 1,
"name": "Yatharth",
"version": "1.1",
"email": "yatharth#test.com"
}
and From Staus viewset i have POST the status of Email: yatharth#test.com to Active. From GET of Status VIewset I want to display data like this:
{
"name": "Yatharth",
"version": "1.1",
"email": "yatharth#test.com"
"status": "Active"
}
How to do this?
I found select_related and prefetch_related in Django ORM but don't know how to use them
From your database, I do not find any reason to create separate table for Status. If you still have to create separate table, you should manage relationship between these two using ForeignKey or OneToOneField. It will make your task a lot easy. For your current structure, you will have to write your serializer like this
class SchemaSerializer(serializer.ModelSerializer):
status = serializer.SerializerMethodField()
def get_status(self, instance):
status = Status.objects.filter(email=instance.email)
if status.exists():
return status.first().status
class Mete:
model = Schema
fields = ('name', 'version', 'email', 'status')
In simple word, create a SerializerMethodField and define its logic of getting data on the basis of Schema object.
If you change your model
class Schema(models.Model):
"""Database model for Schema """
name= models.TextField()
version = models.TextField()
email = models.EmailField(unique = True )
def __str__(self):
return self.email
class Status(models.Model):
""" Model For status """
schema = models. OneToOneField(to=Schema, related_name='schema_status')
status = models.TextField()
def __str__(self):
return self.email
This structre will help you accessing respective Status object of Schema like schema_obj.schema_status where schema_status is related_name. You should keep in mind that this will work only work when you have an object of Status against a Schema object. Therefore I have put if hasattr(instance, 'schema_status') in below serialzer get_status method.
Serializer
class SchemaSerializer(serializer.ModelSerializer):
status = serializer.SerializerMethodField()
def get_status(self, instance):
if hasattr(instance, 'schema_status'):
return instance.schema_status.status
class Mete:
model = Schema
fields = ('name', 'version', 'email', 'status')

Django (REST framework): How to censor columns when user is not logged in

I am using Django as my CMS and React for my Frontend.
For the API I am using Django REST framework.
In Django I have a model like this:
class person(models.Model):
number = models.IntegerField(primary_key=True)
name = models.CharField(max_length=128)
last_name = models.CharField(max_length=128)
I would like to censor some columns depending on if the user sent me the correct Authentification Token or not.
So in result I would like to have something like
{ "number": 1, "name": "johnny", "last_name": "miagi"}
when the user is authentificated, and
{ "number": 1, "name": "johnny", "last_name": "########"}
when not.
Is this possible? I am, unfortunately, a beginner in Django. I know about the user privileges system, but as far as I know this is meant to be used on complete tables, not columns.
You can censor fields by your own by modifying the serializer this way:
class PersonSerializer(serializers.ModelSerializer):
last_name = serializers.SerializerMethodField('get_last_name')
class Meta:
model = Person
fields = ('__all__')
def get_last_name(self, obj):
request = getattr(self.context, 'request', None)
if not request.user:
return "########"
By using this, you can also do user-level censors to last_name field by putting conditional statements in your get_last_name() method.
make 2 serializers or in serializer make a condition when user loged in then use one serializer else use another serializer.
class ModelApiView(generics.ListCreateAPIView):
"""
List of User Model History api
"""
def get_serializer_class(self):
if self.request.user is None or self.request.user is Anonymous:
return serializer_one
else:
return serializer_two
queryset = Model.objects.all()
class serializer_two(serializers.ModelSerializer):
class Meta:
model = Model
fields = "__all__"
class serializer_one(serializers.ModelSerializer):
field_value = serializers.SerializerMethodField('get_field_value')
class Meta:
model = Model
fields = ('__all__')
def get_field_value(self, obj):
return "########"

Update data using RetrieveUpdateAPIView - Getting validated data from a serializer

I would like to update certain properties of a user (say first_name and last_name)
my json object through a PUT request would look like this
{
"user" : {
"first_name": "Jack",
"last_name": "shnider",
"password":"admin123"
"email" : "foo#google.com"
},
"employee_zip" : 12345
}
This is what my view looks like (I would like to update the existing fields to these new fields).
These are the serializer
class Serializer_UpdateUser(ModelSerializer):
class Meta:
model = User
fields = ('first_name','last_name','password')
class Serializer_UpdateEmployer(ModelSerializer):
user = Serializer_UpdateUser()
class Meta:
model = modelEmployer
fields = [
'user',
'employer_zip',
]
This is the view :
class UpdateProfile_RetrieveUpdateAPIView(RetrieveUpdateAPIView):
queryset = modelEmployer.objects.all()
serializer_class = Serializer_UpdateEmployer
lookup_field = 'user__email'
permission_classes = [permissions.AllowAny]
def update(self, request, *args, **kwargs):
instance = self.get_object() #------>I have the object that I would like to update
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True) #--->Success
Now I would like to get a validated fields (The json only contains the fields that have been updated). I know if I do something like this
serializer.save
I would get back a modelEmployer but instead I get back this error
AssertionError at /api/employer/update_profile/employerA#gmail.com/ The `.update()` method does not support writable nested fields by default. Write an explicit `.update()` method for serializer `Employer.api.serializers.Serializer_ListEmployer`, or set `read_only=True` on nested serializer fields. Request Method:
I have two questions
1-Why is save failing ?
2-How can I get the validated data from the above serializer ?
The save is failing because django-rest-framework doesn't deal with nested serializers by default.
from the django-rest-framework docs:
By default nested serializers are
read-only. If you want to support write-operations to a nested
serializer field you'll need to create create() and/or update()
methods in order to explicitly specify how the child relationships
should be saved.
You have to override the update method in the serializer to allow that behavior:
class Serializer_UpdateEmployer(ModelSerializer):
user = Serializer_UpdateUser()
class Meta:
model = modelEmployer
fields = [
'user',
'employer_zip',
]
def update(self, instance, validated_data):
user_data = validated_data.pop('user', {})
user_serializer = Serializer_UpdateUser(instance.user, data=user_data)
user_serializer.save()
return instance
Another solution is to use drf-writable-nested. It automatically makes your nested serializers updatable.
from drf_writable_nested import WritableNestedModelSerializer
class Serializer_UpdateEmployer(WritableNestedModelSerializer):
user = Serializer_UpdateUser()
class Meta:
model = modelEmployer
fields = [
'user',
'employer_zip',
]
I think drf-writable-nested can help you to update nested data.
In you case:
from django.contrib.auth import password_validation
class Serializer_UpdateUser(ModelSerializer):
def update(self, instance, validated_data):
password = validated_data.pop('password', None)
super(Serializer_UpdateUser, self).update(instance, validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
def validate_password(self, value):
password_validation.validate_password(value)
return value
class Meta:
model = User
fields = ('first_name','last_name','password')
class Serializer_UpdateEmployer(WritableNestedModelSerializer):
user = Serializer_UpdateUser()
class Meta:
model = modelEmployer
fields = [
'user',
'employer_zip',
]
Note you need special handling password field.