Как вывести выборку объектов Blog в ListView в Django с поиском по ForeignKeys двух других различных моделей?
У меня есть три такие модели:
1:
class Blog(models.Model):
title = models.CharField()
published = models.DateField()
...
2:
class AuthorList(models.Model):
blog = models.ForeignKey(Blog)
author = models.ForeignKey(User)
lastTimeEdited = models.DateTimeField()
...
3:
class CommentsList(models.Model):
blog = models.ForeignKey(Blog)
commentAuthor = models.ForeignKey(User)
commentPosted = models.DateTimeField()
...
Мой ListView, основанный на классе, выглядит так:
class DashboardView(LoginRequiredMixin, ListView):
model = Blog
template_name = 'app/dashboard.html'
paginate_by = 5
def get_queryset(self) -> QuerySet[Blog]:
user = self.request.user
author = #HERE I want a queryset of all blogs, where the requested user is one of the authors.
commentator = #HERE I want a queryset of all blogs, where the requested user is one of the commentators.
blogs = author|commentator
return blogs.order_by(#HERE I want to order them by the lastTimeEdited-Field or the commentPosted-Field)
Итак, я хочу вывести список всех блогов, к которым так или иначе имеет отношение пользователь. Я также хочу упорядочить их по значению lastTimeEdited, если пользователь является автором, или по значению commentPosted, если пользователь является комментатором. Как мне это сделать?
Я уже искал решение в других постах, но не смог найти правильное решение для моей проблемы.
Your AuthorList
model acts as a junction table for a many-to-many relation between Blog
and User
. You can span a ManyToManyField
[Django-doc] on the Blog
model with:
class Blog(models.Model):
# …
authors = models.ManyToManyField(
settings.AUTH_USER_MODEL,
through='AuthorList',
related_name='authored_blogs',
)
commenters = models.ManyToManyField(
settings.AUTH_USER_MODEL,
through='CommentsList',
related_name='commented_blogs',
)
Это значительно упрощает составление запросов, так как мы можем работать с:
from django.db.models import Q
class DashboardView(LoginRequiredMixin, ListView):
model = Blog
template_name = 'app/dashboard.html'
paginate_by = 5
def get_queryset(self) -> QuerySet[Blog]:
return Blog.objects.filter(
Q(authors=request.user) | Q(commenters=request.user)
).order_by(???)
Единственная проблема теперь - как упорядочить, мы можем определить последнего автора и/или время комментария с помощью .annotate(…)
[Django-doc] и затем использовать это:
from django.db.models import Greatest, Max, Q
class DashboardView(LoginRequiredMixin, ListView):
model = Blog
template_name = 'app/dashboard.html'
paginate_by = 5
def get_queryset(self) -> QuerySet[Blog]:
return (
Blog.objects.filter(
Q(authors=request.user) | Q(commenters=request.user)
)
.annotate(
latest_edit=Greatest(
Max('authorlist__lastTimeEdited'),
Max('commentslist__lastTimeEdited'),
)
)
.order_by('-latest_edit')
)
For databases like SQLite, Oracle and MySQL, this will not be sufficient, since if there the user is not an author, or a commenter, it will return NULL
, only if you are both author and commenter, that will work. We can fix this in a very ugly way with Coalesce
[Django-doc]:
from django.db.models import Coalesce, Greatest, Max, Q
class DashboardView(LoginRequiredMixin, ListView):
model = Blog
template_name = 'app/dashboard.html'
paginate_by = 5
def get_queryset(self) -> QuerySet[Blog]:
return (
Blog.objects.filter(
Q(authors=request.user) | Q(commenters=request.user)
)
.annotate(
latest_edit=Greatest(
Coalesce(
Max('authorlist__lastTimeEdited'),
Max('commentslist__lastTimeEdited'),
),
Coalesce(
Max('commentslist__lastTimeEdited'),
Max('authorlist__lastTimeEdited'),
),
)
)
.order_by('-latest_edit')
)
Note: It is normally better to make use of the
settings.AUTH_USER_MODEL
[Django-doc] to refer to the user model, than to use theUser
model [Django-doc] directly. For more information you can see the referencing theUser
model section of the documentation [Django-doc].