I have this model of Article
where users can post articles, add comments, etc. I want to add a filter where it can filter Articles
according to user which has visited the last Article
or commented on the last Article
.
Please guide me on how can I achieve this functionality in Django/DRF .
This is my models:
class Article(models.Model):
id=models.AutoField(primary_key=True, auto_created=True, verbose_name="ARTICLE_ID")
headline=models.CharField(max_length=250)
abstract=models.TextField(max_length=1500, blank=True)
content=models.TextField(max_length=10000, blank=True)
files=models.ManyToManyField('DocumentModel', related_name='file_documents',related_query_name='select_files', blank=True)
published=models.DateField(auto_now_add=True, null=True)
tags=models.ManyToManyField('Tags', related_name='tags', blank=True)
isDraft=models.BooleanField(blank=True, default=False)
isFavourite=models.ManyToManyField(User, related_name="favourite", blank=True)
created_by=models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="articles")
def get_id(self):
return self.headline + ' belongs to ' + 'id ' + str(self.id)
class Meta:
verbose_name_plural= "Articles"
ordering=("id", "headline", "abstract", "content", "published", "isDraft", "created_by")
def __str__(self):
return f'{self.headline}'
EDITED
This is the following I have implemented
Articles.py
class Article(models.Model):
id=models.AutoField(primary_key=True, auto_created=True, verbose_name="ARTICLE_ID")
headline=models.CharField(max_length=250)
abstract=models.TextField(max_length=1500, blank=True)
content=models.TextField(max_length=10000, blank=True)
files=models.ManyToManyField('DocumentModel', related_name='file_documents',related_query_name='select_files', blank=True)
published=models.DateField(auto_now_add=True, null=True)
tags=models.ManyToManyField('Tags', related_name='tags', blank=True)
isDraft=models.BooleanField(blank=True, default=False)
isFavourite=models.ManyToManyField(User, related_name="favourite", blank=True)
created_by=models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="articles")
def get_id(self):
return self.headline + ' belongs to ' + 'id ' + str(self.id)
def seen_by_user(self, user):
return self.articleseenrecord_set.objects.filter(user=user).exists()
@register.simple_tag
def article_seen_by_user(article, user):
return article.seen_by_user(user)
class Meta:
verbose_name_plural= "Articles"
ordering=("id", "headline", "abstract", "content", "published", "isDraft", "created_by")
def __str__(self):
return f'{self.headline}'
This is I created my ArticleSeenRecord
:
class ArticleSeenRecord(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
Here is how I tried to serialize:
serializers.py
class ArticleSerializer(serializers.ModelSerializer):
#serializer for getting username of User
created_by=serializers.CharField(source='created_by.username', read_only=True)
files=DocumentSerializer(many=True, read_only=True)
isFavourite=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
tags=serializers.SlugRelatedField(many=True, queryset=Tags.objects.all(), slug_field="tag")
seen_by_user=serializers.CharField(read_only=True)
class Meta:
model= Article
fields = ["id", "headline", "abstract", "seen_by_user", "content", "published", "isDraft", "isFavourite", "tags", 'files', 'created_by' ]
After checking I get the following error in response:
{
"id": 1,
"headline": "Article 1",
"abstract": "Abstract 1",
"seen_by_user": "<bound method Article.seen_by_user of <Article: Article 1>>",
"content": "content 1",
"published": "2022-10-20",
"isDraft": true,
"isFavourite": [
1
],
"tags": [
"Python",
"C++"
],
"files": [
{
"id": 1,
"document": "http://127.0.0.1:8000/uploaded_files/6Dp4gG4pi8Wm4ficssTD6o.pdf",
"filesize": "122 KB",
"filename": "resume.pdf",
"mimetype": "application/pdf",
"created_at": "2022-10-20"
}
],
"created_by": "abdullahroshan"
},
I get "seen_by_user": "<bound method Article.seen_by_user of <Article: Article 1>>"
error in response.
Also, I don't know how to use this template tag and check that the user has viewed an article.
This is Articles
viewset:
class ArticleViewSet(viewsets.ModelViewSet):
serializer_class=ArticleSerializer
permission_classes=[permissions.IsAuthenticated]
authentication_classes = [authentication.TokenAuthentication]
pagination_class=StandardResultSetPagination
filter_backends= (DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter)
filterset_fields= ['headline', 'isDraft']
search_fields=['headline']
ordering_fields = ['id']
def get_object(self, queryset=None):
obj=super().get_object(queryset)
record, created = ArticleSeenRecord.objects.get_or_create(user=self.request.user, article=obj)
return obj
UPDATED
Hi, thanks for the update, I have implemented every step, and there is currently an error that is popping
After implementing a serializer like this
ArticleSerializer
class ArticleSerializer(serializers.ModelSerializer):
#serializer for getting username of User
created_by=serializers.CharField(source='created_by.username', read_only=True)
files=DocumentSerializer(many=True, read_only=True)
isFavourite=serializers.PrimaryKeyRelatedField(many=True, read_only=True)
tags=serializers.SlugRelatedField(many=True, queryset=Tags.objects.all(), slug_field="tag")
seen_by_user=serializers.SerializerMethodField()
class Meta:
model= Article
fields = ["id", "headline", "seen_by_user", "abstract", "content", "published", "isDraft", "isFavourite", "tags", 'files', 'created_by' ]
def get_seen_by_user(self, instance):
return instance.seen_by_user(article, self.context['request'].user)
And in Views addling like this:
class ArticleViewSet(viewsets.ModelViewSet):
serializer_class=ArticleSerializer
permission_classes=[permissions.IsAuthenticated]
authentication_classes = [authentication.TokenAuthentication]
pagination_class=StandardResultSetPagination
filter_backends= (DjangoFilterBackend, filters.OrderingFilter, filters.SearchFilter)
filterset_fields= ['headline', 'isDraft']
search_fields=['headline']
ordering_fields = ['id']
def get_object(self, queryset=None):
obj = super().get_object(queryset)
record, created=ArticleSeenRecord.objects.get_or_create(user=self.request.user, article=obj)
return obj
def get_serializer_context(self):
context = super().get_serializer_context()
context.update({"request": self.request})
return context
I get the below error:
return instance.seen_by_user(article, self.context['request'].user)
NameError: name 'article' is not defined
Here the article is not defined.
Please see my answer here from a previous question, which is almost identical.
You could create an extra table.
class ArticleSeenRecord(models.Model):
user = models.ForeignKey("django.contrib.auth.models.User", on_delete=models.CASCADE)
article = models.ForeignKey(Article, on_delete=models.CASCADE)
And then in your article view, create a new record when one doesn't exist, for that article combined with the authenticated user.
class ArticleDetail(LoginRequiredMixin, DetailView):
model = Article
context_object_name = 'articles'
template_name = 'home/article_detail.html'
def get_object(self, queryset=None):
obj = super().get_object(queryset)
record, created = ArticleSeenRecord.objects.get_or_create(user=self.request.user, article=obj)
return obj
class Article(models.Model):
...
def seen_by_user(self, user):
return self.atricleseenrecord_set.objects.filter(user=user).exists()
I added the extra function here. You will also need to add a template tag which you can ideally copy from this example
@register.simple_tag
def article_seen_by_user(article, user):
return article.seen_by_user(user)
For further guidance on how to use and register custom template tags, please refer to this page of the documentation:
https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/
Specifically this section:
https://docs.djangoproject.com/en/4.0/howto/custom-template-tags/#django.template.Library.simple_tag
EDIT Thanks for your responses and for updating your answer, I understand your point and will respond based on the expanded question.
You have pretty much implemented perfectly, the only difference would be on your serialiser - you should change the seen_by_user
field from a CharField
to:
seen_by_user = serialisers.SerializerMethodField()
And this will solve your problem of not getting the seen_by_user
field properly serialised. Then you can define a function on the serialiser, for example:
def get_seen_by_user(self, instance):
return instance.seen_by_user(article, self.context['request'].user)
and finally, you can add the request into the serialiser context like so:
class MyModelViewSet(ModelViewSet):
queryset = MyModel.objects.all()
permission_classes = [DjangoModelPermissions]
serializer_class = MyModelSerializer
def get_serializer_context(self):
context = super().get_serializer_context()
context.update({"request": self.request})
return context
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.