Update if exists or create Model in Django Rest - django

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

Related

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'])

Filter model with serializer.validated_data

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

Django rest framework get or create for PrimaryKeyRelatedField

I start to create REST API for my web-application with Django and Django rest framework and I need one logic problem.
There are entities Instruction and Tag. The user visit my service and create self Instruction and add exists Tag OR new Tag for it.
I created my model seriallizer class with using PrimaryKeyRelatedField for relation Instruction<->Tag. But if I do POST for a new Instruction with new Tag I got error: "Invalid pk \"tagname\" - object does not exist.".
I solved this problem with the overriding of the to_internal_value method in my field class.
What is the best practice for solving this problem? It seems to me this problem is typical for web and REST API.
My models:
class Tag(Model):
name = CharField(max_length=32, verbose_name=_("Name"),
unique=True, validators=[alphanumeric], primary_key=True)
def __str__(self):
return self.name
class Instruction(Model):
user = ForeignKey(settings.AUTH_USER_MODEL,
related_name='instructions',
on_delete=CASCADE,
blank=False, null=False,
verbose_name=_("User"))
title = CharField(max_length=256,
verbose_name=_("Title"),
blank=False, null=False)
created_datetime = DateTimeField(verbose_name=_("Creation time"), editable=False)
modified_datetime = DateTimeField(
verbose_name=_("Last modification time"), blank=False, null=False)
tags = ManyToManyField(Tag,
related_name="instructions",
verbose_name=_("Tags"))
class Meta:
ordering = ['-created_datetime']
# singular_name = _("")
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
n = now()
if self.id is None:
self.created_datetime = n
self.modified_datetime = n
super(Instruction, self).save(force_insert, force_update, using, update_fields)
def __str__(self):
return self.title
my serializers:
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = ('name',)
class InstructionSerializer(serializers.ModelSerializer):
tags = PrimaryKeyCreateRelatedField(many=True, queryset=Tag.objects.all())
author = serializers.SerializerMethodField()
def get_author(self, obj):
return obj.user.username
class Meta:
model = Instruction
fields = ('id', 'user', 'title', 'created_datetime', 'modified_datetime', 'tags', 'author')
read_only_fields = ('modified_datetime',)
I created new field class class PrimaryKeyCreateRelatedField and overrided to_internal_value method for creating the new Tag object instead raising with message 'does_not_exist':
PrimaryKeyCreateRelatedField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
if self.pk_field is not None:
data = self.pk_field.to_internal_value(data)
try:
return self.get_queryset().get(pk=data)
except ObjectDoesNotExist:
# self.fail('does_not_exist', pk_value=data)
return self.get_queryset().create(pk=data)
except (TypeError, ValueError):
self.fail('incorrect_type', data_type=type(data).__name__)
my view:
class InstructionViewSet(viewsets.ModelViewSet):
queryset = Instruction.objects.all()
serializer_class = InstructionSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def create(self, request, *args, **kwargs):
data = dict.copy(request.data)
data['user'] = self.request.user.pk
serializer = InstructionSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Update
models.py
alphanumeric = RegexValidator(r'^[0-9a-zA-Z]*$',
_('Only alphanumeric characters are allowed.'))
class Tag(Model):
name = CharField(max_length=32, verbose_name=_("Name"),
unique=True, validators=[alphanumeric], primary_key=True)
def __str__(self):
return self.name
class Step(PolymorphicModel):
instruction = ForeignKey(Instruction,
verbose_name=_("Instruction"),
related_name='steps',
blank=False, null=False,
on_delete=CASCADE)
position = PositiveSmallIntegerField(verbose_name=_("Position"), default=0)
description = TextField(verbose_name=_("Description"),
max_length=2048,
blank=False, null=False)
class Meta:
verbose_name = _("Step")
verbose_name_plural = _("Steps")
ordering = ('position',)
unique_together = ("instruction", "position")
def __str__(self):
return self.description[:100]
class Instruction(Model):
user = ForeignKey(settings.AUTH_USER_MODEL,
related_name='instructions',
on_delete=CASCADE,
blank=False, null=False,
verbose_name=_("User"))
title = CharField(max_length=256,
verbose_name=_("Title"),
blank=False, null=False)
created_datetime = DateTimeField(verbose_name=_("Creation time"), editable=False)
modified_datetime = DateTimeField(
verbose_name=_("Last modification time"), blank=False, null=False)
tags = ManyToManyField(Tag,
related_name="instructions",
verbose_name=_("Tags"))
# thumbnail = #TODO: image field
class Meta:
ordering = ['-created_datetime']
# singular_name = _("")
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
n = now()
if self.id is None:
self.created_datetime = n
self.modified_datetime = n
super(Instruction, self).save(force_insert, force_update, using, update_fields)
def __str__(self):
return self.title
views.py
class InstructionViewSet(viewsets.ModelViewSet):
queryset = Instruction.objects.all()
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def get_serializer_class(self):
"""Return different serializer class for different action."""
if self.action == 'list':
return InstructionSerializer
elif self.action == 'create':
return InstructionCreateSerializer
serialiers.py
class PrimaryKeyCreateRelatedField(serializers.PrimaryKeyRelatedField):
def to_internal_value(self, data):
if self.pk_field is not None:
data = self.pk_field.to_internal_value(data)
try:
return self.get_queryset().get(pk=data)
except ObjectDoesNotExist:
# self.fail('does_not_exist', pk_value=data)
return self.get_queryset().create(pk=data)
except (TypeError, ValueError):
self.fail('incorrect_type', data_type=type(data).__name__)
class InstructionCreateSerializer(serializers.ModelSerializer):
tags = PrimaryKeyCreateRelatedField(many=True, queryset=Tag.objects.all())
steps = InstructionStepSerializer(many=True)
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Instruction
fields = ('id', 'user', 'title', 'created_datetime', 'modified_datetime', 'tags', 'steps')
read_only_fields = ('modified_datetime',)
def create(self, validated_data):
tags_data = validated_data.pop('tags')
steps_data = validated_data.pop('steps')
# NOTE: tags need add after creation of the Instruction object otherwise we will got exception:
# "needs to have a value for field "id" before this many-to-many relationship can be used."
instruction = Instruction.objects.create(**validated_data)
for tag in tags_data:
instruction.tags.add(tag)
for step in steps_data:
Step.objects.create(instruction=instruction,
description=step['description'],
position=step['position'])
return instruction
class InstructionSerializer(serializers.ModelSerializer):
tags = serializers.StringRelatedField(many=True)
author = serializers.SerializerMethodField()
steps = InstructionStepSerializer(many=True)
def get_author(self, obj):
return obj.user.username
class Meta:
model = Instruction
fields = ('id', 'user', 'title', 'created_datetime', 'modified_datetime', 'tags', 'author', 'steps')
read_only_fields = ('modified_datetime',)
In my case to solve the problem I need to override the method run_validation. That allow make check of tags and create their (if not exists) before validation.
class InstructionCreateSerializer(serializers.ModelSerializer):
steps = InstructionStepSerializer(many=True)
user = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Instruction
fields = ('title', 'created_datetime', 'modified_datetime', 'tags', 'steps', 'id', 'user')
read_only_fields = ('modified_datetime',)
def run_validation(self, data=serializers.empty):
if 'tags' in data:
for tag in data['tags']:
Tag.objects.get_or_create(name=tag)
return super(InstructionCreateSerializer, self).run_validation(data)
def create(self, validated_data):
tags_data = validated_data.pop('tags')
steps_data = validated_data.pop('steps')
# NOTE: tags need add after creation of the Instruction object otherwise we will got exception:
# "needs to have a value for field "id" before this many-to-many relationship can be used."
instruction = Instruction.objects.create(**validated_data)
for tag in tags_data:
instruction.tags.add(tag)
for step in steps_data:
Step.objects.create(instruction=instruction,
description=step['description'],
position=step['position'])
return instruction
Apart from the answers given by #YPCrumble and #SijanBhandari, I just had to comment on something in your code.
In the models.py, you have overridden the save method for adding created_at and modified_on. For that you could just add
created_at = models.DateTimeField(auto_now_add=True)
modified_on = DateTimeField (auto_now=True)
The auto_now_add option sets when the object is created for the first time.
It's not editable. The auto_now setting sets whenever the object is saved, ie, whenever object.save() method is called upon.
These usually are used for timestamping the objects for future references.
Why write so many lines, when you could do this on just 2 lines of code.
Just a heads up though!!
For further details, go to the documentation here
In "regular" Django you usually want to create your model instance in the form's save method, not the view. DRF is similar, in that you want to create your model instances in the serializer's create or update methods. The reason for this is that if you need to add a new endpoint to your API you can reuse the serializer and would not have to write duplicate code creating or updating your model instance.
Here's how I'd refactor your code:
Remove the entire create method from your ModelViewSet - you don't need to override that.
Remove the custom PrimaryKeyCreateRelatedField - you just need a PrimaryKeyRelatedField
Add two methods to your serializer - create and update:
In the create method, create your tag objects before saving the instruction object like you can see in the DRF docs. You can get the current user like you were doing in your view via self.context['request'].user in this create method. So you might create the Instruction like Instruction.objects.create(user=self.context['request'].user, **validated_data) and then loop through the tags (like they do for tracks in the docs) to add them to the Instruction.
The docs don't have an example update method but essentially your update method also takes an instance parameter for the existing instruction. See this answer from the creator of DRF for more details
The best way would be sort out everything at your CREATE method of the view.
I believe you tags will be sent from your front-end to the back-end at the format of
[ 1,
{'name': "TEST"},
{'name': 'TEST2'}
]
Here '1' is the existing tag id and 'TEST' and 'TEST2' are the two new tags inserted by
the user. Now you can change your CREATE method as follows:
class InstructionViewSet(viewsets.ModelViewSet):
queryset = Instruction.objects.all()
serializer_class = InstructionSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
def create(self, request, *args, **kwargs):
data = dict.copy(request.data)
data['user'] = self.request.user.pk
# MODIFICATION.....
tags = self.request.get('tags', None)
tag_list = []
if tags:
for tag in tags:
if isinstance(tag, dict):
new_tag = Tag.objects.create(name=tag['name'])
tag_list.append(new_tag.id)
else:
tag_list.append(int(tag))
data = {
'title': ....
'tags': tag_list,
'user': ...
'author': ...
......
}
serializer = InstructionSerializer(data=data)
I hope it will be helpful for you.

How do I add a field to a django-rest-framework serializer that isn't on my model?

I have a serializer that works fine for the GET, POST, DELETE actions. It exposes the model fields that I want. However for the PUT action, the user will send back values that aren't built into my models and the server will deal with how to perform the update on the model. I can send the data back using Postman or Curl and it works but the browseable API still looks like this:
For the PUT method I want "is_winner", "num_hands_won", and "score" to show up instead of the actual model fields. How do I do this? (Let me know in the comments if you need more info)
StatisticsSerializer:
class StatisticsSerializer(serializers.ModelSerializer):
# pk = serializers.IntegerField(required=False)
class Meta:
model = Statistics
fields = [
'url',
'games_won',
'hands_won',
'games_played',
'high_score',
'low_score',
]
Statistics Model:
class Statistics(models.Model):
# Define model fields:
user = models.OneToOneField(User, primary_key=True)
games_won = models.IntegerField(null=True, blank=True)
hands_won = models.IntegerField(null=True, blank=True)
games_played = models.IntegerField(null=True, blank=True)
high_score = models.IntegerField(null=True, blank=True)
low_score = models.IntegerField(null=True, blank=True)
def __str__(self):
return str(self.pk)
def increment_games_won(self, is_winner):
if is_winner is True:
self.games_won = self.games_won + 1
return self.games_won
def add_to_hands_won(self, num_hands_won):
if num_hands_won > 0 and num_hands_won < 8:
self.hands_won = self.hands_won + num_hands_won
return self.hands_won
def increment_games_played(self):
self.games_played = self.games_played + 1
return self.games_played
def new_high_score(self, score):
if score > self.high_score:
self.high_score = score
return self.high_score
def new_low_score(self, score):
if score < self.low_score:
self.low_score = score
return self.low_score
Statistics ViewSet:
class StatisticsViewSet(DefaultsMixin, viewsets.ModelViewSet):
queryset = Statistics.objects.all()
serializer_class = StatisticsSerializer
filter_class = StatisticsFilter
search_fields = ('pk', 'user')
ordering_fields = ('games_won', 'hands_won', 'games_played', 'high_score', 'low_score')
def update(self, request, pk=None):
stats = self.get_object()
stats.increment_games_won(request.data['is_winner'])
stats.add_to_hands_won(request.data['num_hands_won'])
stats.increment_games_played()
stats.new_low_score(request.data['score'])
stats.new_high_score(request.data['score'])
stats.save()
serialized_stats = StatisticsSerializer(stats, context={'request': request}).data
return Response(serialized_stats)
You could probably use another Serializer and use it for you PUT API
StatisticsUpdateSerializer:
class StatisticsUpdateSerializer:
is_winner = ...
num_hands_won = ...
score = ...
And use this serializer in the PUT API or create a new route as shown in the example mentioned in the DRF documentation here
#detail_route(methods=['post'])
def set_password(self, request, pk=None):
user = self.get_object()
// Use your serializer below
serializer = PasswordSerializer(data=request.data)
if serializer.is_valid():
user.set_password(serializer.data['password'])
user.save()
return Response({'status': 'password set'})
else:
return Response(serializer.errors,
status=status.HTTP_400_BAD_REQUEST)

DRF serializer get liked data with post by request user

I'm making a social app like facebook.
when getting post(at news feed) data I would like to get Boolean if I pressed like about that post.
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Post(models.Model):
uploader = models.ForeignKey(User)
likes = models.IntegerField(default=0)
point = models.IntegerField(default=0)
isPointReceived = models.BooleanField(default=False)
content = models.TextField()
uploadedTime = models.DateTimeField(auto_now=True)
# editedTime = models.DateTimeField()
def __str__(self):
return ("[uploader = " + self.uploader.username + "]") + (", [id = " + (str)(self.id) + "]") + ("[content = " + self.content[:50] + "]")
class PostLike(models.Model):
post = models.ForeignKey(Post, related_name='postLikes')
liker = models.ForeignKey(User)
def __str__(self):
return "Like" + "| [Post = " + (str)(self.post) + "]" + ", [Liker = " + self.liker.username + "]"
serializers.py
class PostLikeSerializer(serializers.ModelSerializer):
class Meta:
model = PostLike
fields = '__all__'
class PostListSerializer(serializers.ModelSerializer):
uploader = UserDetailSerializer()
isMine = serializers.SerializerMethodField()
isLiked = serializers.SerializerMethodField()
postComments = PostCommentSerializer(many=True, allow_null=True)
class Meta:
model = Post
fields = ('uploader', 'id', 'likes', 'point', 'isPointReceived', 'content', 'uploadedTime', 'postComments', 'isMine', 'isLiked',)
def get_isMine(self, obj):
requestUser = CurrentUserDefault()
return obj.objects.fileter(uploader=requestUser).exists()
# return obj.filter(uploader=requestUser)
def get_isLiked(self, obj):
requestUser = CurrentUserDefault()
return PostLike.objects.filter(post=obj, liker=requestUser).exists()
#
# try:
# PostLike.objects.get(post=obj, liker=requestUser)
# return True
# except:
# return False
I tried lot of dirty stuffs. but there was no solution..
conclusion:
HTF to get data about, if user has a record of PostLike(post=post, user=user)
how to get user in serializer Class.
or should I approach this in different ways?
like managing liked data in APIView or whatever etc...
help!
========EDITED==========
final code should look like this.
in serialziers.py
class PostListSerializer(serializers.ModelSerializer):
uploader = UserDetailSerializer()
postComments = PostCommentSerializer(many=True, allow_null=True)
postLikes = PostLikeSerializer(many=True, allow_null=True)
postImages = PostImageSerializer(many=True, allow_null=True)
isMine = serializers.SerializerMethodField()
isLiked = serializers.SerializerMethodField()
class Meta:
model = Post
fields = ('uploader', 'id', 'likes', 'content', 'uploadedTime', 'postComments', 'postLikes', 'postImages', 'isMine', 'isLiked',)
def get_isMine(self, obj):
requestUser = self.context['request'].user
return obj.uploader == requestUser
def get_isLiked(self, obj):
requestUser = self.context['request'].user
return PostLike.objects.filter(post=obj, liker=requestUser).exists()
You can get a user from serializer context inside serializer method:
self.context['request'].user
It passed from a method get_serializer_context which originally created in a GenericAPIView:
class GenericAPIView(APIView):
....
def get_serializer_context(self):
"""
Extra context provided to the serializer class.
"""
return {
'request': self.request,
'format': self.format_kwarg,
'view': self
}
As you can see you could also get format and view from context. You could also override this method for set additional context.
def get_isLiked(self, obj):
requestUser = self.context['request'].user
return PostLike.objects.filter(post=obj, liker=requestUser).exists()
If results have 12 objects it will execute 12 similar query, which increase increase your response time. I am looking for solution so that I can make it with one query. Don't know how can I do that.
def get_isLiked(self, obj):
requestUser = self.context['request'].user
return PostLike.objects.filter(post=obj, liker=requestUser).exists()
You should to get all PostLike fields in cache and use prefetch_related method in your query set. This thing will check all entries using cache, not multiple queries.
For example:
def get_isLiked(self, obj):
requestUser = self.context['request'].user
likes = PostLike.objects.all()
return likes.filter(post=obj, liker=requestUser).exists()
and somewhere in models or views or managers in "get_queryset" you should add:
queryset.prefetch_related('postlike')