skip to Main Content

I have a Post model:

class Post(models.Model):
profile = models.ForeignKey(Profile, related_name="posts", on_delete=CASCADE)
project = models.ForeignKey(Project, related_name="posts", on_delete=CASCADE)
title = models.CharField(verbose_name="Title", max_length=150)
body = models.TextField(verbose_name="Content", max_length=1300)
image_url = models.URLField(max_length=256, null=True, blank=True)
liked_by = models.ManyToManyField(Profile, related_name="post_likes")
date_published = models.DateTimeField(auto_now_add=True, verbose_name="Date published")
date_modified = models.DateTimeField(auto_now=True, verbose_name="Date modified")
is_active = models.BooleanField(verbose_name="Active", default=True)
tags = TaggableManager(blank=True, related_name="posts")

objects = models.Manager()

It has a "liked_by" M2M relations to track profiles liked a post.

The goal is to get a Post object with given ID and attach a data of profiles liked this post (id, username, photo).

For this moment my query is :

        post = Post.objects.filter(pk=post_pk)
                       .select_related("profile", "project") 
                       .prefetch_related("liked_by") 
                       .annotate(comments_count=Count("comments"),
                                 likes_count=Count("liked_by"),
                                 users_liked=ArrayAgg("liked_by"))
                       .first()

Later it packed to DTO (Dataclass), serializes and responses for API call.

DTO Class:

@dataclass(frozen=True)
class PostDetailDTO:
    id: str | int
    profile_id: str | int
    project_id: str | int
    title: str
    body: str
    image_url: str
    tags: Set[str]
    date_published: datetime | None
    likes_count: int
    comments_count: int
    users_liked: List[dict] | None = None
    is_active: bool = True

Serializer:

class PostDetailDTOSerializer(serializers.Serializer):
    id = serializers.IntegerField(required=False, read_only=True)
    profile_id = serializers.IntegerField(read_only=True)
    project_id = serializers.IntegerField(read_only=True)
    title = serializers.CharField(max_length=150, read_only=True)
    body = serializers.CharField(max_length=1300, read_only=True)
    image_url = serializers.URLField(read_only=True)
    tags = serializers.ListSerializer(child=serializers.CharField(), read_only=True)
    date_published = serializers.DateTimeField(read_only=True)
    users_liked = serializers.ListSerializer(child=serializers.CharField(), read_only=True)
    likes_count = serializers.IntegerField(read_only=True)
    comments_count = serializers.IntegerField(read_only=True)
    is_active = serializers.BooleanField(read_only=True)

Example of API response for this query:

{
    "post": {
        "id": 25,
        "profile_id": 2,
        "project_id": 1,
        "title": "STATUS  TEST",
        "body": "testing filtering",
        "image_url": null,
        "tags": [],
        "date_published": "2023-07-06T12:44:37.134234Z",
        "users_liked": [
            "2",
            "4"
        ],
        "likes_count": 2,
        "comments_count": 0,
        "is_active": false
    }
}

The question is : how to make it display profile data in format [{"id": 10, "username": "hello_world", "photo": "www.url.com}, {"id":20, "username": "my_username", "photo": "www.another.com"}] instead of just ID?
Probably there is a way through dataclass modifications by using "for in" through "liked_by" field but I’m looking if there is more easier way to perform it within Database/ORM. Database in use – PostgreSQL.

UPDATE:
Updated with ProfileDTO and Serializer.
ProfileDTO:
@dataclass(frozen=True)
class ProfileDTO:
id: int | None
first_name: str
last_name: str
username: str
linkedin_url: str
about: str
photo: str
rating: float
cv_file: str
# subscripted_projects: ProjectDTO
role_id: int
specialization_id: int
tools: list[ToolDTO]

Profile serializer:

class ProfileDTOSerializer(serializers.Serializer):
id = serializers.IntegerField
first_name = serializers.CharField()
last_name = serializers.CharField()
username = serializers.CharField()
linkedin_url = serializers.URLField()
photo = serializers.ImageField()
about = serializers.CharField()
rating = serializers.FloatField()
cv_file = serializers.FileField()
role_id = serializers.IntegerField()
specialization_id = serializers.IntegerField()
tools = serializers.PrimaryKeyRelatedField(queryset=Tools.objects.all(), many=True)

2

Answers


  1. Chosen as BEST ANSWER

    Thanks to @Geilmaker for pointing on some django feature regarding serialization of a outcome data I found a solution with a third party lib. As I use a Dataclass (DTO) to transfer data to API (and vise versa) I found usefull an "auto-dataclass" lib which maps the query object(s) to existing dataclass (model object and dataclass fields names must match). Then I just used a relataion model's serializer as a field of my model's serializer and I got whole relation object data nested in my model's object as well.


  2. Do I get it right, that you query the posts successfully, use a serializer to get the json

    {
        "post": {
            "id": 25,
            "profile_id": 2,
            "project_id": 1,
            "title": "STATUS  TEST",
            "body": "testing filtering",
            "image_url": null,
            "tags": [],
            "date_published": "2023-07-06T12:44:37.134234Z",
            "users_liked": [
                "2",
                "4"
            ],
            "likes_count": 2,
            "comments_count": 0,
            "is_active": false
        }
    }
    

    and now you just want the

    "users_liked": [
        "2",
        "4"
    ],
    

    section instead looking like

    "users_liked": [
        {"id": 2, "username": "hello_world", "photo": "www.url.com"},
        {"id": 4, "username": "my_username", "photo": "www.another.com"}
    ]
    

    If that is correct, you just need to adjust your serializer for the posts.
    You need to have a serializer for your User model, that is serializing the model to {"id": 2, "username": "hello_world", "photo": "www.url.com"}. If you have that Serializer (e.g. UserSerializer), you can adjust your PostSerializer like followed:

    from rest_framework import serializers
    
    class PostSerializer(serializers.ModelSerializer):
        users_liked = UserSerializer(many=True, read_only=True) // This will serialize the ids to the json objects
    
        class Meta:
            model = Post
            fields = '__all__'
    

    Let me know if that helped, if you need more assistance or if this is not at all what you asked about! 🙂

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search