Python Django REST框架:间接相关属性的SlugRelatedField?

Python Django REST框架:间接相关属性的SlugRelatedField?,python,django,serialization,django-rest-framework,Python,Django,Serialization,Django Rest Framework,我有一个配置文件模型,它与Django的用户模型有一对一的关系,我还有另一个模型,叫做权限(与Django内部的权限概念无关),它有一个外键来配置文件。这样:(为了简单起见,我删除了这里的大部分字段) 我想为权限创建一个序列化程序,它将创建如下对象: { "user": "me@example.com", "account": 123 } 其中,“account”的值是帐户的主键(这样很容易,我可以使用PrimaryKeyRelatedField),而“user”的值是用户的电子邮件地

我有一个配置文件模型,它与Django的用户模型有一对一的关系,我还有另一个模型,叫做权限(与Django内部的权限概念无关),它有一个外键来配置文件。这样:(为了简单起见,我删除了这里的大部分字段)

我想为权限创建一个序列化程序,它将创建如下对象:

{
  "user": "me@example.com",
  "account": 123
}
其中,“account”的值是帐户的主键(这样很容易,我可以使用PrimaryKeyRelatedField),而“user”的值是用户的电子邮件地址(这是我还没有弄清楚的部分,因为电子邮件地址不是直接存储在Profile对象上的,而是存储在关联的DjangoUser对象上)。还请注意,这不是只读的-创建权限时,它确实需要能够从电子邮件地址反序列化到配置文件对象

到目前为止,我已经尝试了一些方法,用于在权限序列化程序上表示用户

一,

对于这一个,如果我发布一个电子邮件地址(或主键或任何其他内容)作为“用户”,它会返回一个400错误,说
{“user”:“此字段是必需的。”}
,好像我根本没有包含此字段

二,

有了这个,我得到了AttributeError:“Profile”对象没有属性“django\u user.email”。如果我使用
“django\u用户\u电子邮件”
,也会发生同样的情况


有什么想法吗?

你能试试:
user=serializers.SlugRelatedField(slug\u field='django\u user\u email')
?有时在Django中,查询的双下划线表示法可能很有用。
来源:

为了更好地理解您的问题,我花时间编写了一些代码。如果希望在
权限序列化程序
上完全支持CRUD操作,则需要嵌套
配置文件
对象,以便读取其属性。 这些是我建议您实现的序列化程序

from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Permission, Profile, Account


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email',)


class ProfileSerializer(serializers.ModelSerializer):

    django_user = UserSerializer(many=False)

    class Meta:
        model = Profile
        fields = ('django_user',)


class PermissionSerializer(serializers.ModelSerializer):

    user = ProfileSerializer(many=False)

    class Meta:
        model = Permission
        fields = ('user', 'account')
输出为:

{
        "user": {
            "django_user": {
                "username": "admin",
                "email": "admin@mail.com"
            }
        },
        "account": 1
    }

希望这能帮助您,如果这是一种有用的方法,并且您想让它成为可写的look at()。

根据Django REST Framework序列化程序参考():

将用于填充字段的属性的名称。可以是只接受自参数的方法,例如URLField(source='get_absolute_url'),也可以使用点符号遍历属性,例如EmailField(source='user.email')

因此,如果
source='user.email'
不起作用,您可以尝试创建一个方法(例如
get\u user\u email
),在
source
字段中使用该方法,并使其手动返回用户电子邮件。

来源

将用于填充字段的属性的名称。也许 是只接受
self
参数的方法,例如
URLField(source='get\u absolute\u url')
,也可以使用虚线表示法 遍历属性,例如
EmailField(source='user.email')

然而,在我的项目中从未有过使用点符号的成功尝试。我总是使用实例方法或属性来代替。

最近,我深入研究了
ModelSerializer
,发现了一些有趣的东西:

def build_field(self, field_name, info, model_class, nested_depth):
    """
    Return a two tuple of (cls, kwargs) to build a serializer field with.
    """
    if field_name in info.fields_and_pk:
        model_field = info.fields_and_pk[field_name]
        return self.build_standard_field(field_name, model_field)

    elif field_name in info.relations:
        relation_info = info.relations[field_name]
        if not nested_depth:
            return self.build_relational_field(field_name, relation_info)
        else:
            return self.build_nested_field(field_name, relation_info, nested_depth)

    elif hasattr(model_class, field_name):
        return self.build_property_field(field_name, model_class)

    elif field_name == self.url_field_name:
        return self.build_url_field(field_name, model_class)

    return self.build_unknown_field(field_name, model_class)
可能处理点符号遍历的部分应该是info.relations中的
elif field\u name

  • 根据我的项目回溯,
    字段名
    可能类似于
    “django\u user.email”
    ,而
    信息
    是带有OrderedDict的
    关系
    属性的命名偶

  • info.relations
    OrderedDict的键是
    ForeignKey
    s和
    ManyToManyField
    s的字段名

  • 所以实际上,点符号遍历不会由这个来处理


最后,点符号遍历由
返回self.build\u unknown\u field(…)
处理。

问题明确指出,这会导致AttributeError。如原始问题中所述,该字段不是只读的。它需要能够反序列化,而不仅仅是序列化。使用
source
并不意味着它是只读的。当您尝试反序列化时会发生什么?
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Permission, Profile, Account


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('username', 'email',)


class ProfileSerializer(serializers.ModelSerializer):

    django_user = UserSerializer(many=False)

    class Meta:
        model = Profile
        fields = ('django_user',)


class PermissionSerializer(serializers.ModelSerializer):

    user = ProfileSerializer(many=False)

    class Meta:
        model = Permission
        fields = ('user', 'account')
{
        "user": {
            "django_user": {
                "username": "admin",
                "email": "admin@mail.com"
            }
        },
        "account": 1
    }
def build_field(self, field_name, info, model_class, nested_depth):
    """
    Return a two tuple of (cls, kwargs) to build a serializer field with.
    """
    if field_name in info.fields_and_pk:
        model_field = info.fields_and_pk[field_name]
        return self.build_standard_field(field_name, model_field)

    elif field_name in info.relations:
        relation_info = info.relations[field_name]
        if not nested_depth:
            return self.build_relational_field(field_name, relation_info)
        else:
            return self.build_nested_field(field_name, relation_info, nested_depth)

    elif hasattr(model_class, field_name):
        return self.build_property_field(field_name, model_class)

    elif field_name == self.url_field_name:
        return self.build_url_field(field_name, model_class)

    return self.build_unknown_field(field_name, model_class)