【问题标题】:Related Object serializer Django restframework相关的对象序列化器 Django rest 框架
【发布时间】:2018-07-09 14:26:00
【问题描述】:

我的问题与this one 有一些不同之处。我有一个类似于这个的模型:

class Project(models.Model):
    project_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False, unique=True)
    created_by_id = models.ForeignKey('auth.User', related_name='project', on_delete=models.SET_NULL, blank=True, null=True)
    created_by = models.CharField(max_length=255, default="unknown")
    created = models.DateTimeField(auto_now_add=True)

使用以下序列化程序:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = serializers.ReadOnlyField(source='created_by_id.username')

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created')

以及对应的视图:

class projectsView(mixins.ListModelMixin,
                  mixins.CreateModelMixin,
                  generics.GenericAPIView):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)

    def post(self, request, *args, **kwargs):
        return self.create(request, *args, **kwargs)

    def perform_create(self, serializer):
        serializer.save(created_by_id=self.request.user)

此代码的行为与我想要的一样,但会强制信息冗余并且不利用底层关系数据库。我尝试使用链接问题中的信息在平面 json 中实现“在数据库上写入用户 id,但在“get”上返回用户名”但没有成功:

删除模型中的“created_by”字段。将序列化程序替换为:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User


class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', write_only=True)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

这不会 100% 给我我想要的,即用平面 json 中的用户名替换用户 ID,但返回类似:{'project_id': <uuid>, 'created_by': <user json object>, 'created': <data>}。但我仍然收到{'created_by_id': ['This field is required.']} 400 错误。

问题:如何将用户 ID 从 request.user 信息写入数据库对象以引用实际用户 ID,但在 projectsView 端点上的 GET 请求中返回简单用户名,而不在模型中显式存储用户名?或者更一般地说,如何使用默认的序列化 DRF 功能和默认的 DRF 视图 mixins 将数据库对象(Django 模型)序列化为客户 json 响应?

问题的另一种表述:如何在我的模型中存储对另一个 DB 记录的 ID 引用(可以在没有有效负载提供的情况下访问它)但在序列化程序级别反序列化来自该对象引用的派生信息,例如作为被引用对象的一个​​特定字段?

【问题讨论】:

  • 如果您想存储当前用户,请将其从序列化程序中删除,然后从视图中填充您的模型。如果这是你的答案,请告诉我完成它
  • 看我贴的sn-p视图。听起来你根本没有回答这个问题。
  • 我的意思是为什么你对 post 和 get 使用相同的序列化程序,你可以为 get 编写另一个序列化程序,其中一个为用户带来所有细节,对于 post 你也可以删除 user_id然后用另一种方法存储在数据库中
  • 我对 DRF 序列化程序的理解是,有一种方法可以从中定义序列化和反序列化,并从视图中抽象出模型,这与您的建议完全相反。但我可能误解了一些东西......

标签: django serialization django-models django-rest-framework


【解决方案1】:

我建议您对GetPOST 操作使用两个不同的序列化程序。将您的 serializers.py 更改为

class ProjectGetSerializer(serializers.ModelSerializer):
    created_by_id = serializers.StringRelatedField()

    class Meta:
        model = Project
        fields = '__all__'


class ProjectCreateSerializer(serializers.ModelSerializer):
    created_by_id = serializers.PrimaryKeyRelatedField(queryset=User.objects.all(), default=serializers.CurrentUserDefault())

    def create(self, validated_data):
        return Project.objects.create(**validated_data, created_by=validated_data['created_by_id'].username)

    class Meta:
        model = Project
        fields = '__all__'


另外,如果您正在寻找CRUD operations,我推荐ModelViewSet 用于API 类。因此,视图将是这样的,

class projectsView(viewsets.ModelViewSet):
    queryset = Project.objects.all()

    def get_serializer_class(self):
        if self.action == 'create':
            return ProjectCreateSerializer
        return ProjectGetSerializer


所以,创建Project 的有效载荷是,

{
}

您应该记住一件事,当您尝试创建 Project 时,用户必须登录

更新 - 1
serializer.py

class ProjectCreateSerializer(serializers.ModelSerializer):
    created_by_id = serializers.StringRelatedField()

    class Meta:
        model = Project
        fields = '__all__'

    def create(self, validated_data):
        return Project.objects.create(**validated_data, created_by_id=self.context['request'].user)

views.py

class projectsView(viewsets.ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectCreateSerializer

【讨论】:

  • 创建你写的payload是错误的。您的观点缺少我的关键方面。用户参考来自谁“登录”而不是来自有效负载。
  • 也就是说,如果你POST空数据到API,它应该也识别created_by..对吗?
  • 一个空的 POST 创建一个用户 ref、一个项目 id 和一个项目创建日期时间。换句话说,“create”有效载荷是空的。
  • 您可以通过覆盖ProjectCreateSerializer 序列化程序的create() 方法来实现。
  • 因此有效载荷将是一个空的 dict/JSON 对象,如 {}
【解决方案2】:

错误出现在 write_only 字段选项中。 required 参数默认值设置为True,而我们查看模型的目的是不要求它。在视图中,我使用 perform_create 作为后期处理来保存模型数据库表示。由于required 在创建级别默认值为True,因此第一个.save() 到DB 失败。由于这纯粹是内部逻辑,因此不需要。因此,只需在 PrimaryKeyRelatedField 上添加 required=False 选项即可:

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User


class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', write_only=True, required=False)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

如果我坚持纯粹在序列化器级别使用逻辑进行反序列化,那么在模型级别强制要求=True 也需要覆盖序列化器的 .save 函数。可能有一种方法可以在序列化程序中获取用户引用,以使视图实现更加“默认”...这可以通过使用来自Jerin 的默认值来完成:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = UserSerializer(read_only=True)
    created_by_id = serializers.PrimaryKeyRelatedField(
    queryset=User.objects.all(), source='created_by', 
                           write_only=True,
                           required=False,
                           default=serializers.CurrentUserDefault())

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

现在要仅使用用户名扁平化 json,您需要使用 slug 字段而不是 UserSerializer:

class ProjectSerializer(serializers.ModelSerializer):

    created_by = serializers.SlugRelatedField(
      queryset=User.objects.all(), slug_field="username")
    created_by_id = serializers.PrimaryKeyRelatedField(
      queryset=User.objects.all(), source='created_by', write_only=True, required=False)

    class Meta:
        model = Project
        fields = ('project_id', 'created_by', 'created_by_id', 'created')

然后只有用户模型的用户名字段值将显示在 get payload 的 create_by json 标记处。

更新 - 1

经过更多的调整,这是我想出的最终版本:

class ProjectSerializer(serializers.ModelSerializer):

    created_by_id = serializers.PrimaryKeyRelatedField(
        queryset=User.objects.all(), write_only=True, required=False, default=serializers.CurrentUserDefault())
    created_by = serializers.SerializerMethodField('creator')

    def creator(self, obj):
        return obj.created_by_id.username

    class Meta:
        model = Project
        fields = ('project_id', 'created_by_id', 'created_by', 'created')

【讨论】:

    猜你喜欢
    • 2015-02-25
    • 1970-01-01
    • 1970-01-01
    • 2015-06-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-22
    • 2015-10-29
    相关资源
    最近更新 更多