Filter model with serializer.validated_data - django

I am setting up a Django REST application where peopple can review restaurants. So far I have those models:
class RestaurantId(models.Model):
maps_id = models.CharField(max_length=140, unique=True)
adress = models.CharField(max_length=240)
name = models.CharField(max_length=140)
class RestaurantReview(models.Model):
review_author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StarterPics(models.Model):
restaurant_review_id = models.OneToOneField(RestaurantReview,
on_delete=models.CASCADE)
pics_author = models.ForeignKey(User, on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
name_1 = models.CharField(max_length=40)
picture_1 = models.ImageField()
My serializers:
class RestaurantIdSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantId
field = fields = '__all__'
class RestaurantReviewSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantReview
field = fields = '__all__'
class StarterPicsSerializer(serializers.ModelSerializer):
class Meta:
model = StarterPics
fields = '__all__'
def validate_restaurant_review_id(self, value)
if value.review_author != self.request.user:
raise serializers.ValidationError("User has not reviewed the restaurant")
return value
My views:
class RestaurantIdViewset(viewsets.ModelViewSet):
queryset = models.RestaurantId.objects.all()
serializer_class = serializers.RestaurantIdSerializer
class RestaurantReviewViewset(viewsets.ModelViewSet):
queryset = models.RestaurantReview.objects.all()
serializer_class = serializers.RestaurantReviewSerializer
permission_classes = [IsAuthenticatedOrReadOnly,IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(review_author=self.request.user)
class StarterPicsViewset(viewsets.ModelViewSet):
queryset = models.StarterPics.objects.all()
serializer_class = serializers.StarterPicsSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
I have set up permissions as well so only the review_author can update his reviews and pics_author can update his pictures.
My permissions:
class IsOwnReviewOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.pics_author == request.user
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.review_author == request.user
Now what I have is that posting a StarterPics on a review that someone is not the author is imposible. That is the behaviour I am looking for. But if the review author tries to do so I have this error:
TypeError: Field 'id' expected a number but got <RestaurantReview: 8>.
Here is my serializer.validated_data:
OrderedDict([('name_1', 'Salade de saison'), ('picture_1', <InMemoryUploadedFile: fricoteurs_inside.jpeg (image/jpeg)>),
('name_2', ''), ('picture_2', None), ('lat_pic_2', None), ('lng_pic_2', None),
('shot_time_2', None), ('restaurant_review_id', <RestaurantReview: 8>), ('pics_author', <User: pi>), ('restaurant_id', <RestaurantId: Les Fricoteurs>)])
Because I cannot call .save() after accessing serializer.data I can't get the 'restaurant_review_id': 4 I would have if I was using serializer.data.
So how can I filter my RestaurantReview model using serializer.validated_data?

This validation should be done in serializer field level, not in perform_create() method:
class StarterPicsSerializer(serializers.ModelSerializer):
...
def validate_resturant_review_id(self, value):
if value.review_user != self.context['request'].user:
raise serializers.ValidationError("User has not reviewed the resturant")
return value

Related

Django: How to add user(author of the POST request) before save in serializers?

I'm tried to add author of the request before saving it in serializers.py
And got error:
Cannot assign "<django.contrib.auth.models.AnonymousUser object at 0x0000029169C48040>": "Category_product.posted_user" must be a "User" instance.
models.py
class Category_product(models.Model):
category_name = models.CharField(max_length=200, unique=True)
posted_user = models.ForeignKey(User, on_delete=models.CASCADE, default=1)
def __str__(self):
return self.category_name
views.py
class Category_productDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Category_product.objects.all()
serializer_class = Category_productSerializer
serializers.py
class Category_productSerializer(serializers.ModelSerializer):
post_user = serializers.ReadOnlyField(
source='posted_user.username')
class Meta:
model = Category_product
fields = ['id', 'category_name','post_user']
def validate(self, data):
data['posted_user'] = self.context['request'].user
return data
Have you tried overriding the user in your view?
Assuming you have Authentacion set up, because if not everyone could get in and it won't be a user, it would be Anonymous User instance.
If that's not the case, you can try it out in the view with perform create:
class Category_productDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Category_product.objects.all()
serializer_class = Category_productSerializer
def get_user(self):
user = self.request.user
return user
def perform_create(self, serializer):
"""
Set the sender to the logged in user.
"""
serializer.save(posted_user=self.get_user())

Field level validation with request.user in Django rest framework

I am setting up a Django REST application where peopple can review restaurants. So far I have those models:
class RestaurantId(models.Model):
maps_id = models.CharField(max_length=140, unique=True)
adress = models.CharField(max_length=240)
name = models.CharField(max_length=140)
class RestaurantReview(models.Model):
review_author = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class StarterPics(models.Model):
restaurant_review_id = models.OneToOneField(RestaurantReview,
on_delete=models.CASCADE)
pics_author = models.ForeignKey(User, on_delete=models.CASCADE)
restaurant_id = models.ForeignKey(RestaurantId, on_delete=models.CASCADE)
name_1 = models.CharField(max_length=40)
picture_1 = models.ImageField()
My serializers:
class RestaurantIdSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantId
field = fields = '__all__'
class RestaurantReviewSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantReview
field = fields = '__all__'
class StarterPicsSerializer(serializers.ModelSerializer):
class Meta:
model = StarterPics
fields = '__all__'
def validate_restaurant_review_id(self, value)
if value.review_author != self.request.user:
raise serializers.ValidationError("User has not reviewed the restaurant")
return value
My views:
class RestaurantIdViewset(viewsets.ModelViewSet):
queryset = models.RestaurantId.objects.all()
serializer_class = serializers.RestaurantIdSerializer
class RestaurantReviewViewset(viewsets.ModelViewSet):
queryset = models.RestaurantReview.objects.all()
serializer_class = serializers.RestaurantReviewSerializer
permission_classes = [IsAuthenticatedOrReadOnly,IsAuthorOrReadOnly]
def perform_create(self, serializer):
serializer.save(review_author=self.request.user)
class StarterPicsViewset(viewsets.ModelViewSet):
queryset = models.StarterPics.objects.all()
serializer_class = serializers.StarterPicsSerializer
permission_classes = [IsAuthenticatedOrReadOnly]
I have set up permissions as well so only the review_author can update his reviews and pics_author can update his pictures.
My permissions:
class IsOwnReviewOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.pics_author == request.user
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.review_author == request.user
When running Django server I got a 'StarterPicsSerializer' object has no attribute 'request'
This validation is for user that have not written the review (review_author) can't POST pictures in StarterPics. So only the User that creates the review can post pictures on it.
I've tried another validation with no luck either:
def validate_restaurant_review_id(self, value):
if not RestaurantReview.objects.filter(restaurant_review_id=value,
review_author=self.request.user).exists():
raise serializers.ValidationError('Not your review')
return value
You could provide extra context to the serializer in addition to the object being serialized by passing a context argument when instantiating the serializer in your view.
serializer = RandomSerializer(instance, context={'request': request})
If you use Generic Views or ModelViewSet(inherited form GenericAPIView), then request is already available in your serializer self.context dict
class StarterPicsSerializer(serializers.ModelSerializer):
class Meta:
model = StarterPics
fields = '__all__'
def validate_restaurant_review_id(self, value):
print(self.context['request'])

How to join models in Python djangorestframework

I am trying to joint two models in django-rest-framework.
My code isn't throwing any error but also it isn't showing other model fields that need to be joined.
Below is my code snippet:
Serializer:
class CompaniesSerializer(serializers.ModelSerializer):
class Meta:
model = Companies
fields = ('id', 'title', 'category')
class JobhistorySerializer(serializers.ModelSerializer):
companies = CompaniesSerializer(many=True,read_only=True)
class Meta:
model = Jobhistory
fields = ('id', 'title', 'company_id', 'companies')
View .
class UserJobs(generics.ListAPIView):
serializer_class = JobhistorySerializer()
def get_queryset(self):
user_id = self.kwargs['user_id']
data = Jobhistory.objects.filter(user_id=user_id)
return data
model:
class Companies(models.Model):
id = models.AutoField(primary_key=True)
title = models.CharField(max_length=100, blank=True, default='')
category = models.CharField(max_length=30, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
title = self.title or False
category = self.category or False
super(Companies, self).save(*args, **kwargs)
class Jobhistory(models.Model):
id = models.AutoField(primary_key=True)
company_id = models.ForeignKey(Companies)
title = models.CharField(max_length=100, blank=True, default='')
created = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ('created',)
def save(self, *args, **kwargs):
company_id = self.company_id or False
title = self.title or False
super(Jobhistory, self).save(*args, **kwargs)
Thanks in advance. Any help will be appreciated.
In your views, you have
serializer_class = JobHistorySerializer()
Remove the parenthesis from this.
The reason for this is apparent in the GenericAPIView, specifically the get_serializer() and get_serializer_class() methods:
def get_serializer(self, *args, **kwargs):
"""
Return the serializer instance that should be used for validating and
deserializing input, and for serializing output.
"""
serializer_class = self.get_serializer_class()
kwargs['context'] = self.get_serializer_context()
return serializer_class(*args, **kwargs)
def get_serializer_class(self):
"""
Return the class to use for the serializer.
Defaults to using `self.serializer_class`.
You may want to override this if you need to provide different
serializations depending on the incoming request.
(Eg. admins get full serialization, others get basic serialization)
"""
assert self.serializer_class is not None, (
"'%s' should either include a `serializer_class` attribute, "
"or override the `get_serializer_class()` method."
% self.__class__.__name__
)
return self.serializer_class
As you can see in get_serializer, it initializes that serializer class with args and kwargs that aren't provided in your view code.

Update if exists or create Model in Django Rest

I got this model:
class Like(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
post = models.ForeignKey('posts.Post', blank=True, null=True)
RATING_CONVERSION = (
(1, '+'),
(0, '0'),
(-1, '-'),
)
userRating = models.SmallIntegerField(choices=RATING_CONVERSION)
def __int__(self):
return self.id
If post id and user id exists I need to update rating.
I try to make it.
Serializer:
class LikeSerializer(serializers.ModelSerializer):
user = serializers.PrimaryKeyRelatedField(queryset=ExtUser.objects.all(), required=False, allow_null=True,default=None)
class Meta:
model = Like
field = ('user', 'post')
validators = [
UniqueTogetherValidator(
queryset=Like.objects.all(),
fields=('user', 'post')
)
]
def validate_user(self, value):
return self.context['request'].user
def create(self, validated_data):
return Like.objects.create(**validated_data)
And ViewSet
class LikeViewSet(viewsets.ModelViewSet):
queryset = Like.objects.all()
serializer_class = LikeSerializer
#detail_route(methods=['post', 'get'])
def get_object(self):
if self.request.method == 'POST':
like = Like.objects.get(user=self.context['request'].user, post = self.context['request'].post)
if like:
return like
else:
return Like(id=self.kwargs.get('pk'))
else:
return super(LikeViewSet, self).get_object()
I found a lot of information how to create or update using pk in Url, but I got parameters inside of JSON
This method doesn't work - instead of update rating it creates new Model objects

Find where in Django code an 'incorrect type error' is being thrown for a POST

I am trying to discover where in my Django code an incorrect type error message is being thrown:
{"_body":"{\"keywords\":[\"Incorrect type. Expected pk value, received str.\"]}","status":400,"statusText":"Ok","headers":{"Content-Type":["application/json;q=0.8"]},"type":2,"url":"http://127.0.0.1:8000/api/items/"}
I have put a breakpoint in the view's perform_create that handles the POST but this is never executed.
Anyone know where in my code I should set a breakpoint to take a look at what is happening?
My view looks like this:
class ItemViewSet(viewsets.ModelViewSet):
queryset = Item.objects.all().order_by('-date_added')
serializer_class = ItemSerializer
def list(self, request, *args, **kwargs):
this_user = self.request.query_params.get('user', None)
count_of_views = Seen.objects.filter(who_saw=this_user).count()
custom_data = {
'results': ItemSerializer(self.get_queryset(), many=True).data
}
custom_data.update({
'views': count_of_views
})
return Response(custom_data)
def perform_create(self, serializer):
creator = User.objects.get(pk=self.request.data['owner_id'])
the_keywords = self.request.data['keywords'].split
serializer.save(owner=creator)
#serializer.save(keywords=the_keywords)
EDIT: And the serializer is:
class ItemSerializer(serializers.ModelSerializer):
username = serializers.SerializerMethodField()
def get_username(self, obj):
value = str(obj.owner)
return value
def get_keywords(self, obj):
value = str(obj.keywords)
return value
class Meta:
model = Item
fields = ('id', 'url', 'item_type', 'title', 'keywords')
EDIT Item model is:
class Item(models.Model):
owner = models.ForeignKey(User, on_delete=models.CASCADE)
title = models.CharField(max_length=60, default='')
url = models.CharField(max_length=250, default='', unique=True)
keywords = models.ManyToManyField(Keyword, related_name='keywords')
Keyword model is:
class Keyword(models.Model):
name = models.CharField(max_length=30)
def __str__(self):
return format(self.name)