I have a Django Model where users can create objects and keep them private for a certain period of time.
class MyModel(models.Model):
creator = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
private_until = models.DateField(null=True, default=None, blank=True)
objects = MyModelManager()
In my ListView, I want for visitors only the "non-private" objects to appear and for authenticated users the "non-private" objects plus their own private objects. So my Manager looks like this:
class MyModelManager(models.Manager):
def include_his_private(self, user):
if user.is_authenticated:
include_his_private = super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))
include_his_private |= super().get_queryset().filter(Q(creator=user))
return include_his_private
else:
return super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))
def get_queryset(self):
return super().get_queryset().filter(~Q(private_until__gt=datetime.date.today()))
For my ListView
this works great. But when I click on an object to get its DetailView
, I get a 404 error right on "URL-Level". In my URLs I have:
path('<slug:slug>', MyModelDetailView.as_view(), name='mymodel_detail'),
... and somehow, Django checks already in advance the whole slug against the per Manager allowed slugs, before I have the chance to pass the user in. What is the way to solve my problem? Any help is appreciated. Thanks in advance!
EDIT: My DetailView looks like this:
class MyModelDetailView(DetailView):
model = MyModel
template_name = 'mymodel_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
slug = self.kwargs['slug']
if self.request.user.is_authenticated:
obj = MyModel.objects.include_his_private(self.request.user).get(slug=slug)
else:
obj = MyModel.objects.get(slug=slug)
A DetailView
[Django-doc] will use the .get_queryset(…)
[Django-doc] to obtain a queryset and in the .get_object(…)
method [Django-doc], it will filter on the pk
and/or slug
automatically, if a path contains such URL parameters. This is thus done before you run the custom logic in the get_context_data
.
You thus should override the get_queryset
method:
class MyModelDetailView(DetailView):
model = MyModel
template_name = 'mymodel_detail.html'
def get_queryset(self, *args, **kwargs):
return MyModel.objects.include_his_private(
self.request.user
)