Search code examples
pythondjango

I'm trying to ensure that only the Person that will be at an Event will only be able to view the Events they're attending. How do I do this?


I'm using Django for the purposes of this project and I have a Many-to-Many relation between Person and Event (many people can be at many [existing] Events) and I want to make sure that each Person can only view the Events they're attending, not any other.

I'm trying to do this through the admin panel with the use of has_view_permission, but I haven't a clue how to go on about this. I'm trying to use request.user for the purposes of this check.

I know that using a through table with models.ForeignKey also works, but since Django already has that implementation invisible to the end user, I wanted to try this idea with a models.ManyToManyField.

So far, I have this for my models.py:

class Person(models.Model):
    # fields in the model such as id, name, etc.

class Event(models.Model):
    # fields similar to Person - id, name, date, etc.
    attendees = models.ManyToManyField(Person)

and for the admin.py

from django.contrib import admin
from .models import Person, Event

class EventAdmin(admin.ModelAdmin):
    def has_view_permission(self, request, obj=None):
        if Event.objects.filter(attendees = request.user):
            return True
        else:
            return False

However, as one might imagine, this disallows anyone but the people in events to actually view the events, thus giving me a ValueError at the admin panel. Note that I have added some person and event before the function overload.

Upon opening the admin page, I get Cannot query "admin": Must be "Person" instance., which isn't desirable.

My question now is: How can I make it so that the Person (attendees) can only view the events they're attending without preventing everyone else from viewing anything?

If this can be done in a more elegant manner without has_view_permission, then I'm also open to using that method too.


Solution

  • The has_view_permission method is used in both the list and detail views. In the list view, obj is None, while in the detail view, it represents an event instance.

    To ensure that regular users can access the admin panel as staff, we check if they exist using the attendee relationship. When accessing event_instance.attendees, you'll get a manager, not all attendees. To get all attendees, use event_instance.attendees.all().

    To verify if a user exists, we use .filter() and .exists():

    class EventAdmin(admin.ModelAdmin):
        def has_view_permission(self, request, obj=None):
            user=request.user
            if obj is None or  user.is_superuser:
                return True
            return obj.attendees.filter(pk=user.pk).exists()
    

    To filter the list view to show only events that a user can attend, we override the get_queryset method:

    def get_queryset(self, request):
        user=request.user
        if user.is_superuser:
            return qs
        return qs.filter(attendees=user)