Как вывести выборку объектов 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 the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation [Django-doc].

Вернуться на верх