Groupby и Count не включают пользователей, у которых нет сообщений (отношения OneToMany между двумя моделями)

У меня есть две модели, User и Post, где каждый пользователь может иметь много сообщений.

class User(models.Model):
    first_name = models.CharField("First name", max_length=150)
    last_name = models.CharField("Last name", max_length=150)


class Post(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="posts")
    content = models.CharField(max_length=300)

Теперь для примера данные, подобные этим:

User
id    first_name    last_name
1     Jon           Skeet
2     Gordon        Linoff
3     Another       Soul

Post
user  content
1     Jon #1
1     Jon #2
1     Jon #3
2     Gordon #1
2     Gordon #2

Я пытаюсь создать API, который возвращает ответ следующим образом:

[
    {
        "first_name": "Jon",
        "last_name": "Skeet",
        "posts_count": "3",
        "recent_posts": [
            {
                "content": "Jon #1",
                "created": "2022-07-22T07:48:12.299032Z"
            },
            {
                "content": "Jon #2",
                "created": "2022-07-22T07:47:26.830772Z"
            },
            {
                "content": "Jon #3",
                "created": "2022-07-22T07:02:31.654366Z"
            }
        ]
    },
    {
        "first_name": "Gordon",
        "last_name": "Linoff",
        "posts_count": "2",
        "recent_posts": [
            {
                "content": "Gordon #1",
                "created": "2022-07-22T09:59:36.965825Z"
            },
            {
                "content": "Gordon #2",
                "created": "2022-07-22T09:59:18.544077Z"
            },
        ]
    },
    {
        "first_name": "Another",
        "last_name": "Soul",
        "posts_count": "0",
        "recent_posts": []
    }
]

Итак, для каждого пользователя я хочу включить эту информацию в результат:

  1. Количество их сообщений
  2. Три их последних сообщения

Вот что я сделал на данный момент. Я создал два сериализатора, по одному для каждой модели:

class UserSerializer(serializers.ModelSerializer):
    posts_count = serializers.IntegerField(required=False)
    recent_posts = serializers.ListField(required=False)

    class Meta:
        model = User
        fields = ("first_name", "last_name", "posts_count", "recent_posts")


class PostsSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ("content", "created")

Затем я подсчитываю сообщения, и для каждого пользователя я получаю три последних сообщения и возвращаю сериализованные данные:

@api_view(["GET"])
def get_user(request):
    users = (
        User.objects
        .filter(posts__isnull=False)
        .values("id", "first_name", "last_name")
        .annotate(posts_count=Count("id"))
    )

    for user in users:
        recent_addresses = (
            Post.objects
            .filter(user=user["id"])
            .order_by("-created")
        )[:3]

        user["recent_posts"] = PostsSerializer(recent_addresses, many=True).data

    serializer = UserSerializer(users, many=True)
    return Response(serializer.data)

Но результат таков:

[
    {
        "first_name": "Jon",
        "last_name": "Skeet",
        "posts_count": 3,
        "recent_posts": [
            {
                "content": "Jon #3",
                "created": "2022-07-22T14:02:00.928810Z"
            },
            {
                "content": "Jon #2",
                "created": "2022-07-22T14:01:51.328254Z"
            },
            {
                "content": "Jon #1",
                "created": "2022-07-22T14:01:41.935594Z"
            }
        ]
    },
    {
        "first_name": "Gordon",
        "last_name": "Linoff",
        "posts_count": 2,
        "recent_posts": [
            {
                "content": "Gordon #2",
                "created": "2022-07-22T14:02:16.865880Z"
            },
            {
                "content": "Gordon #1",
                "created": "2022-07-22T14:02:08.371927Z"
            }
        ]
    }
]

Как вы можете видеть, он не включал пользователей, у которых нет сообщений. Я могу понять почему, потому что я явно сказал .filter(posts__isnull=False). Но я не знаю, как решить эту проблему. Теперь я не могу удалить этот фильтр, иначе он будет возвращать 1 для каждого posts_count. Я считаю, что усложнил ситуацию больше, чем нужно, с помощью цикла for, но не знаю другого способа. Как я могу решить эту проблему?

Попробуйте это

users = (
        User.objects
        .values("id", "first_name", "last_name")
        .annotate(posts_count=Count("posts"))
    )
Вернуться на верх