Home > Software design >  Show different pages based on user - django
Show different pages based on user - django

Time:05-19

I'm building a web app IMDB's like. Each (logged) user views the same exact homepage, but they can flag/unflag every movie they want to save them in a "seen list". I've done that but I've noticed that every user logged can view the other user's flagged movies. Of course this is a problem, so I'd like to build a page relative to each user, so that everyone would have different "seen films" page based on the movies flagged as seen. The main idea behind this, is that whenever a user adds a movie to the database, he can flag it as seen with a simple models.BooleanField in the model of the film. I have then another model "Seen" where there are all the movies flagged.

models.py

class Films(models.Model):
    ...
    seen = models.BooleanField(default=False)
class Seen(models.Model):
    ...

views.py

    films = Films.objects.all()
    for film in films:
    flag = 0
    seen_films = Seen.objects.all()
    for _ in seen_films:
        if film.name == _.name:
            flag = 1
            break
    if not flag and film.seen:
        act_seen = Seen(image=film.image, name=film.name, description=film.description)
        act_seen.save()

I need to add that for the user, I use the default class provided by django:

from django.contrib.auth.models import User

I get that the error is everyone can flag/unflag a movie and so everyone can see other user's movies but I have no idea how to fix that. Also I think the code in views.py is very raw but it was more a test than the final product. Thank you!

CodePudding user response:

You could extend the default User class (check the docs, there is several ways to do this and the one I use may not be the best one for you) and use a many-to-many to save all the movies that the user has seen:

# models.py
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    films_seen = models.ManyToManyField(Films, related_name='seen_by')

Then you could use a ListAPIView and override the get_queryset() method:

# views.py
from rest_framework.generics import ListAPIView

class FilmsSeenList(ListAPIView):
    permission_classes = (IsAuthenticated,)

    def get_queryset(self):
        return self.request.user.profile.films_seen.all()

Note that you must be sure that each user has a profile associated (with a signal for example, docs here), else get_queryset() would raise a Profile.DoesNotExist:

# handlers.py
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver

from accounts.models import Profile  # Define where your Profile model is here

User = get_user_model()


@receiver(post_save, sender=User, dispatch_uid="create_profile")
def post_save_user(sender, instance, created, **kwargs):
    # Making sure that a profile is assigned for new users
    if created:
        Profile.objects.create(user=instance)

You could also implement a view returning all the films and a flag to know if the user saw the film:

# views.py
from rest_framework.generics import ListAPIView

class AllFilmsList(ListAPIView):
    permission_classes = (IsAuthenticated,)
    serializer_class = FilmsSeenFlagSerializer

    def get_queryset(self):
        return Films.objects.all()


# serializers.py
class FilmsSeenFlagSerializer(serializers.ModelSerializer):
    seen = serializers.SerializerMethodField()

    def get_seen(self, instance):
        return instance.seen_by.filter(pk=self.context['request'].user.pk).exists()

    class Meta:
        model = Films
        fields = [..., 'seen']
  • Related