Reputation: 119
I have a few really simple views in my views.py
.
class IndexView(generic.ListView):
template_name = 'voting/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
return Poll.objects.filter(
pub_date__lte=timezone.now()
).order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Poll.objects.filter(pub_date__lte=timezone.now())
class ResultsView(generic.DetailView):
model = Poll
template_name = 'voting/results.html'
context_object_name = 'question'
I also have created a model
where I store which users are invited to which polls along with some other data.
class EligibleVoters(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
poll = models.ForeignKey(Poll, on_delete=models.CASCADE, null=True)
encrypted_keypart = models.BinaryField(max_length=200, blank=True)
decrypted_keypart = models.BinaryField(max_length=200, blank=True)
class Meta:
unique_together = ["user", "poll"]
I want to restrict users that aren't invited to a poll from seeing any of these polls.
I think that what I want to do is something like this for each view, but I'm not sure if that's the correct way to go.
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
if EligibleVoters.objects.filter(poll=Poll.objects.id, user=self.request.user.id).exists():
return Poll.objects.filter(pub_date__lte=timezone.now())
else:
return render('voting/somethingsomething.html')
})
Should I restrict access to specific polls that way? Also the above code doesn't really work and gives some errors but I'm not sure if I should continue and try to fix it that way.
Upvotes: 1
Views: 324
Reputation: 476537
eligblevoters
The get_queryset
does not render the output, it only produces a queryset.
Nevertheless, we can use this to restrict access, by adding extra filtering:
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_queryset(self):
return super(DetailView, self).get_queryset().filter
eligiblevoters__user=self.request.user,
pub_date__lte=timezone.now()
)
Similar approaches should be used in the other views.
The query works as follows. By defining a ForeignKey
from EligibleVoters
to User
(named user
), then Django creates an implicit relation in reverse: you can for example query with User.eligblevoters_set
to obtain the related EligableVoters
queryset. We can also filter on it with elgiblevoters
.
We thus add two extra filter conditions: pub_date__lte=timezone.now()
to filter posts that are published before now (now included), and eligiblevoters__user=self.request.user
. This means that we add a constraint that at least one of the related eligablevoters
should have as user
the self.request.user
(the user of this specific session).
So in case there is no such Post
, then we can not get the Poll
with the id
that is requested.
We can also render a page in case the object is not found, for example by patching the .get(..)
function with a try
-except
with Http404
, and then render a specific page, for example:
from django.http import Http404
class DetailView(generic.DetailView):
model = Poll
template_name = 'voting/detail.html'
context_object_name = 'question'
def get_queryset(self):
return super(DetailView, self).get_queryset().filter
eligiblevoters__user=self.request.user,
pub_date__lte=timezone.now()
)
def get(request, *args, **kwargs):
try:
return super(DetailView, self).get(request, *args, **kwargs)
except Http404:
return render(request, 'app/template_not_found.html', {})
So here we render a (possibly different template) with here an empty context, although you can of course make this more advanced.
Upvotes: 2
Reputation: 599470
You should add a many-to-many field from Poll to User, using EligibleVoter as a through field:
eligible_users = models.ManyToManyField('User', through='EligibleVoter')
Now you can do:
return Poll.objects.filter(eligible_users=self.request.user, pub_date__lte=timezone.now())
Upvotes: 2