0

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 2

1

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! :)

Sign up to request clarification or add additional context in comments.

8 Comments

Thank You for a quick response. You the point right. But it doesn't work. I've updated a question with Serializer class I use and DTO class. For this moment I got an exception: "Got AttributeError when attempting to get a value for field first_name on serializer ProfileDTOSerializer." I still dont understand how the serializer is able to get the data from an ID which represented as string.
At first I would recommend using the serializers.ModelSerializer class, if you want a model to be serialized - it helps a lot! I can't help you with the error with my current information, because I can't see the ProfileDTOSerializer.
stackoverflow.com/a/39137996/17583953 In this post there is an example, how those 2 serializers could look like and interact with each other. The model knows which model is referenced in a fk-field or m2m-field. If you declare a serializer for that field, it looks up the primary key and uses the given serializer to serialize it. In your case you need to add the many=True like in my example, because you have an array of profiles, that liked that post. I can try to give you more detailed information for your project, but for this I would need some more code snippets to work with...
I have certain restrictions regarding ModelSerializer utilzations. So basic Serializer is only option. So basically I want to get Profile objects in a Post query. That will solve the proble, so I can later serialize the Profile objects with custom Serializer with only 3 fields.
Is it currently possible, to give your PostDetailDTOSerializer a queried instance of your posts and it successfully converts it to your current json?
|
1

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.