'Django: How do I check if a User has already read/seen an article?

I am building a web-application for my senior design project with Python and Django. I have a user model that is able to read/write articles to display on the website. I have some tasks I want to accomplish.

  1. I want to make it so that if an article is accessed (read) by a user, it is indicated for only that user that the article has been previously accessed. If I were to log into a brand new user account, the same article wouldn't be indicated as "accessed" for the new account.

  2. How would I be able to present on the front-end side that the article has been viewed by the user logged in? (ie: make the article title bolded or a different color to indicate its been already visited)

Below are my models and views:

User model

class User(AbstractBaseUser, PermissionsMixin):
    first_name = models.CharField(max_length=30, blank=True)
    last_name = models.CharField(max_length=30, blank=True)
    email = models.EmailField(unique=True, max_length=255, blank=False)
    university = models.CharField(max_length=200, null=True, blank=True)
    newsletter_subscriber = models.BooleanField(default=False)
    is_email_verified = models.BooleanField(default=False)
    is_staff = models.BooleanField(
        default=False,
        help_text=(
            'Designates whether the user can log into '
            'this admin site.'
        ),
    )
    is_active = models.BooleanField(
        default=True,
        help_text=(
            'Designates whether this user should be '
            'treated as active. Unselect this instead '
            'of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(default=timezone.now)
    objects = UserManager()
    USERNAME_FIELD = 'email'

    def __str__(self):
        return self.email

Article model

class Article(models.Model):
    user = models.ForeignKey(
        User, on_delete=models.DO_NOTHING, null=True, blank=True)
    title = models.CharField(max_length=200, null=True, blank=False)
    author = models.CharField(max_length=200, null=True, blank=False)
    year = models.CharField(max_length=200, null=True, blank=False)
    journal = models.CharField(max_length=200, null=True, blank=False)
    description = models.TextField(null=True, blank=True)
    URL = models.CharField(max_length=200, null=True, blank=False)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

    class MetaData:
        ordering = ['-created']

Article detail view

class ArticleDetail(LoginRequiredMixin, DetailView):
    model = Article
    context_object_name = 'articles'
    template_name = 'home/article_detail.html'

Thank you!



Solution 1:[1]

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

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1