Django REST框架:SlugRelatedField用于间接相关属性?

时间:2014-10-02 18:41:15

标签: python django serialization django-rest-framework

我有一个与Django的用户模型有一对一关系的Profile模型,我有另一个名为Permission的模型(与Django的内部权限理念无关), Profile的外键。像这样:(为了简单起见,我已经删除了大部分字段)

from django.db import models
from django.contrib.auth.models import User as DjangoUser

class Account(models.Model):
    name = models.CharField(max_length=200, db_index=True)

class Profile(models.Model):
    django_user = models.OneToOneField(DjangoUser)
    default_account = models.ForeignKey(Account)

class Permission(models.Model):
    # Which user has the permission
    user = models.ForeignKey(Profile, db_index=True)
    # Which account they have the permission on
    account = models.ForeignKey(Account, db_index=True)

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

{
  "user": "me@example.com",
  "account": 123
}

其中" account"的值是帐户的主键(这样很简单,我可以使用PrimaryKeyRelatedField)和" user"是用户的电子邮件地址(这是我尚未弄清楚的部分,因为电子邮件地址不是直接存储在Profile对象上,而是存储在关联的DjangoUser对象上)。另请注意,这不是只读的 - 在创建权限时,它需要能够从电子邮件地址反序列化为Profile对象。

到目前为止我尝试过的一些事情,用于在Permission序列化器上表示用户......

1

user = serializers.RelatedField(read_only=False)

有了这个,如果我将电子邮件地址(或主键或其他任何内容)作为"用户"发布,则会返回400错误{"user": "This field is required."},就好像我没有&#39 ; t包括该字段。

2

user = serializers.SlugRelatedField(slug_field='django_user.email')

有了这个,我得到AttributeError: 'Profile' object has no attribute 'django_user.email'。如果我使用'django_user__email',也会发生同样的事情。

有什么想法吗?

4 个答案:

答案 0 :(得分:2)

根据Django REST Framework序列化程序引用(http://www.django-rest-framework.org/api-guide/fields/#core-arguments):

  

将用于填充字段的属性的名称。可能是一个只接受自身参数的方法,例如URLField(source =' get_absolute_url'),或者可以使用点分表示法来遍历属性,例如EmailField(source =' user.email&# 39。)

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

答案 1 :(得分:0)

你可以尝试:user = serializers.SlugRelatedField(slug_field='django_user__email')吗?有时在Django中,查询的双下划线符号可能很有用。 资料来源:https://docs.djangoproject.com/en/1.8/topics/db/queries/#field-lookups

答案 2 :(得分:0)

我花时间写了一些代码,以便更好地理解你的问题。如果要完全支持PermissionsSerializer上的CRUD操作,则需要嵌套Profile对象,以便可以读取其属性。 这些是我建议你实现的序列化器。

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
    }

希望这可以帮到你,如果这个方法很有用,你想让它可以写一下(http://www.django-rest-framework.org/api-guide/relations/#nested-relationships)。

答案 3 :(得分:0)

根据docs

  

source

     

将用于填充字段的属性的名称。可以   是一个只接受self参数的方法,例如   URLField(source='get_absolute_url'),或者可以使用点分表示法   遍历属性,例如EmailField(source='user.email')

然而,在我的项目中,从未有过使用虚线表示法的成功尝试。我总是最终使用实例方法或属性。

最近我潜入ModelSerializer的{​​{3}},发现了一些有趣的事情:

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)

可能处理虚线表示法遍历的部分应为elif field_name in info.relations

  • 根据我项目的追溯,field_name可能类似于"django_user.email",而info是具有OrderedDict的relations属性的NamedTuple。

  • info.relations OrderedDict的键是ForeignKeyManyToManyField s的字段名称。

  • 实际上,点缀符号遍历不会由此处理。

最后,点缀符号遍历由return self.build_unknown_field(...) source code处理。