首页 文章

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

提问于
浏览
9

我有一个与Django的User模型有一对一关系的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)

有了这个,如果我将电子邮件地址(或主键或其他任何内容)作为"user"发布,它会返回400错误,说 {"user": "This field is required."} ,好像我根本没有包含该字段 .

2 .

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

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

有任何想法吗?

4 回答

  • 2

    根据docs

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

    However, there has never been a successful attempt with the dotted notation in my projects. I always ended up using an instance method or a property instead.

    最近我潜入source codeModelSerializer ,发现了一些有趣的东西:

    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的键是 ForeignKey s和 ManyToManyField 的字段名称 .

    • 所以真的,虚线符号遍历不会被这个处理 .

    最后,点缀符号遍历由 return self.build_unknown_field(...) 处理raised an exception .

  • 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) .

  • 0

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

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

    因此,如果 source='user.email' 无效,您可以尝试创建方法(例如 get_user_email ),在 source 字段中使用它并使其手动返回用户电子邮件 .

  • 0

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

相关问题