简体   繁体   中英

How to implement last visited functionality for articles in django rest framework

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM