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.
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)