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": []
}
]
Итак, для каждого пользователя я хочу включить эту информацию в результат:
- Количество их сообщений
- Три их последних сообщения
Вот что я сделал на данный момент. Я создал два сериализатора, по одному для каждой модели:
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"))
)