How can I serialize a Many to Many field with added data in Django Rest Framework, without extra keys in the response?

huangapple go评论61阅读模式
英文:

How can I serialize a Many to Many field with added data in Django Rest Framework, without extra keys in the response?

问题

I am using Django 3.2 and Django Rest Framework 3.14.

I have Users that should be able to follow other Users, like on a social networking site. When serializing a full User, I would like to get a list of those following Users, as well as additional data, such as the follow datetime. However, I would like the response to stay "flat," like this:

{
	"username":"admin",
	"followers":[
		{
			"username":"testuser",
			--additional user fields--
			"follow_date":"2023-02-08 01:00:02"
		},
		--additional followers--
	],
	--additional user fields--
}

I can only seem to go "through" my join model using an extra serializer, and end up with this:

{
	"username":"admin",
	"followers":[
		{
			"user":{
				"username":"testuser",
				--additional user fields--
			},
			"follow_date":"2023-02-08 01:00:02"
		},
		--additional followers--
	],
	--additional user fields--
}

Note how there is an additional user key:

"user":{
	"username":"testuser",
	--additional user fields--
},

I don't want that there!

My model looks something like this:

class Follower(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="followers")
    follower = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="following")
    follow_date = models.DateTimeField(auto_now_add=True)

My serializers look like this (extra fields trimmed for brevity):

class UserSnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username']

class FollowerSerializer(serializers.ModelSerializer):
    user = UserSnippetSerializer(source='follower')

    class Meta:
        model = Follower
        fields = ['user', 'follow_date']

class FullUserSerializer(serializers.ModelSerializer):
    followers = FollowerSerializer(source='user.followers', many=True)
    following = FollowerSerializer(source='user.following', many=True)

    class Meta:
        model = Profile
        fields = ['username', 'followers', 'following']

Given this, I understand why it's doing what it is. But I can't seem to find any way to skip the extra user key, while still including both the User information AND the follow_date.

I've tried playing around with proxy models on User, but that didn't seem to help. I suspect that this is no configuration thing, though I would like it to be, and that I need to override some internal function. But I can't seem to locate what that would be.

What's the best (and hopefully easiest!) way to accomplish this?

英文:

I am using Django 3.2 and Django Rest Framework 3.14.

I have Users that should be able to follow other Users, like on a social networking site. When serializing a full User, I would like to get a list of those following Users, as well as additional data, such as the follow datetime. However, I would like the response to stay "flat", like this:

{
	"username":"admin",
	"followers":[
		{
			"username":"testuser",
			--additional user fields--
			"follow_date":"2023-02-08 01:00:02"
		},
		--additional followers--
	],
	--additional user fields--
}

I can only seem to go "through" my join model using an extra serializer, and end up with this:

{
	"username":"admin",
	"followers":[
		{
			"user":{
				"username":"testuser",
				--additional user fields--
			},
			"follow_date":"2023-02-08 01:00:02"
		},
		--additional followers--
	],
	--additional user fields--
}

Note how there is an additional user key

"user":{
	"username":"testuser",
	--additional user fields--
},

I don't want that there!

My model looks something like this:

class Follower(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="followers")
    follower = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.RESTRICT, related_name="following")
    follow_date = models.DateTimeField(auto_now_add=True)

My serializers look like this (extra fields trimmed for brevity):

class UserSnippetSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['username']

class FollowerSerializer(serializers.ModelSerializer):
    user = UserSnippetSerializer(source='follower')

    class Meta:
        model = Follower
        fields = ['user', 'follow_date']

class FullUserSerializer(serializers.ModelSerializer):
    followers = FollowerSerializer(source='user.followers', many=True)
    following = FollowerSerializer(source='user.following', many=True)

    class Meta:
        model = Profile
        fields = ['username', 'followers', 'following']

Given this, I understand why it's doing what it is. But I can't seem to find any way to skip the extra user key, while still including bot the User information AND the follow_date.

I've tried playing around with proxy models on User, but that didn't seem to help. I suspect that this is no configuration thing, though I would like it to be, and that I need to override some internal function. But I can't seem to locate what that would be.

What's the best (and hopefully easiest!) way to accomplish this?

答案1

得分: 1

一种方法是在 FollowerSerializer 上使用一个序列化方法字段,像这样:

class FollowerSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()

    class Meta:
        model = Follower
        fields = ['username', 'follow_date']

    def get_username(self, obj):
        return obj.follower.username

或者,如果你需要显示来自用户的多个字段,你也可以像这样重写 to_representation

class FollowerSerializer(serializers.ModelSerializer):
    user = UserSnippetSerializer(source='follower')

    class Meta:
        model = Follower
        fields = ['user', 'follow_date']

    def to_representation(self, instance):
        data = super().to_representation(instance)
        user_data = data.pop("user")

        # 将关注者数据与用户数据展开
        return {
            **data,
            **user_data,
        }

请注意,这也会影响 FullUserSerializer.following

英文:

One approach is to use a serializer method field on FollowerSerializer like this:

class FollowerSerializer(serializers.ModelSerializer):
    username = serializers.SerializerMethodField()

    class Meta:
        model = Follower
        fields = ['username', 'follow_date']

    def get_username(self, obj):
        return obj.follower.username

or if you have lots of fields to display from user, you can also override to_representation like this:

class FollowerSerializer(serializers.ModelSerializer):
    user = UserSnippetSerializer(source='follower')

    class Meta:
        model = Follower
        fields = ['user', 'follow_date']


    def to_representation(self, instance):
        data = super().to_representation(instance)
        user_data = data.pop("user")

        # flatten the follower data with the user data
        return {
            **data,
            **user_data,
        }

Note that this will also affect FullUserSerializer.following

huangapple
  • 本文由 发表于 2023年2月8日 11:16:50
  • 转载请务必保留本文链接:https://go.coder-hub.com/75381079.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定