【问题标题】:Django Rest Framework inserting and updating writable nested serializerDjango Rest Framework 插入和更新可写嵌套序列化程序
【发布时间】:2019-08-10 16:29:45
【问题描述】:

我尝试使用 Django Rest Framework 插入和更新一个可写的嵌套序列化程序,遵循 this 之类的示例。但它不起作用,因为在我执行 serializer.is_valid() 之后,它会丢失来自 serializer.validated_data 的引用,就像它从未发送过一样。

我做错了什么?

我的模型

class User(AbstractUser):

    institution = models.ForeignKey(Institution, on_delete=None, null=True, blank=True)

    class Meta:
        db_table = 'User'
        managed = True
        verbose_name = 'Users'
        verbose_name_plural = 'Users'
        ordering = ['id']

    def __str__(self):
        return self.email

class Institution(models.Model):
    id = models.AutoField(db_column='id', primary_key=True)
    name = models.CharField(db_column='name', max_length=255, null=False)
    country = models.CharField(db_column='country', max_length=255, null=False)

    class Meta:
        db_table = 'Institution'
        managed = True
        verbose_name = 'Institutions'
        verbose_name_plural = 'Institutions'
        ordering = ['id']

    def __str__(self):
        return self.name

我的序列化器

class InstitutionSerializer(serializers.ModelSerializer):

    class Meta:
        model = Institution
        fields = '__all__'
        datatables_always_serialize = ('id', 'name', 'country')

class UserSerializer(serializers.HyperlinkedModelSerializer):
    institution = InstitutionSerializer()

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

    def update(self, instance, validated_data):
        institution_data = validated_data['institution']
        instance.institution = Institution.objects.get(pk=institution_data['id'])
        return instance

    class Meta:
        model = User
        fields = (
            'id',
            'username',
            'first_name',
            'last_name',
            'email',
            'password',
            'is_active',
            'institution',
        )
        datatables_always_serialize = (
            'id',
            'username',
            'first_name',
            'last_name',
            'email',
            'is_active',
            'institution',
        )

我的看法

class UserViewSet(ModelViewSet):
    serializer_class = UserSerializer
    permission_classes = (IsSuperUserPermission,)

    def list(self, request, **kwargs):
        params = Q()
        if 'search[value]' in request.GET and request.GET['search[value]'] != '':
            params = Q(username__icontains=request.GET['search[value]']) |\
                     Q(first_name__icontains=request.GET['search[value]']) |\
                     Q(last_name__icontains=request.GET['search[value]']) |\
                     Q(email__icontains=request.GET['search[value]']) |\
                     Q(institution__name__icontains=request.GET['search[value]'])

        queryset = User.objects.filter(params).select_related().order_by('id')
        serializer = self.serializer_class(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, *args, **kwargs):
        queryset = User.objects.filter(pk=request.GET['pk']).select_related()
        serializer = UserSerializer(queryset, many=True)
        return Response(serializer.data)

    def get_permissions(self):
        if self.action in ('create',):
            self.permission_classes = [AllowAny, ]
        return super(self.__class__, self).get_permissions()

    def create(self, request, *args, **kwargs):
        serializer = UserSerializer(data=request.data)
        if serializer.is_valid():
            serializer.create(serializer.validated_data)
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status.HTTP_500_INTERNAL_SERVER_ERROR)

    def partial_update(self, request, *args, **kwargs):
        user = User.objects.get(pk=request.data['id'])
        serializer = UserSerializer(instance=user, data=request.data, partial=True)
        if serializer.is_valid():

            if 'password' in serializer.validated_data:
                serializer.validated_data['password'] = make_password(serializer.validated_data['password'])

            serializer.update(user, serializer.validated_data)
            return Response(serializer.data)
        else:
            return Response(serializer.errors, status.HTTP_500_INTERNAL_SERVER_ERROR)

编辑。

我这样提交数据:

{
    "username": "BLA",
    "email": "BLA@BLA.com",
    "first_name": "BLA",
    "last_name": "BLA",
    "institution": 1,
    "is_active": true,
    "password": "bla12345"
}

【问题讨论】:

  • 你有什么错误吗?
  • 没有错误,只是没有更新字段
  • 创建时有什么问题吗?
  • 其实它只是清除.is_valid()之后的字段,然后不改变instante的值,没有任何其他错误或异常

标签: django django-rest-framework


【解决方案1】:

为什么会出现这个问题?

在您的更新负载中,您将 institution 数据提供为代表 PK 的整数。但是您还在 UserSerializer() 类中定义了一个嵌套的序列化器 InstitutionSerializer()。因此,DRF 需要一个类似 dict 的对象(DRF 可能会通过提及这一点引发一些错误。我不确定为什么在这种情况下没有发生这种情况)。

解决办法是什么?

由于您传递的是机构 ID,我假设您只需要在HTTP GET 请求中嵌套输出。因此,覆盖 UserSerializer() 类的 __init__() 方法并将嵌套序列化程序的使用限制为仅 HTTP GET 请求

这是代码

class UserSerializer(serializers.HyperlinkedModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.context['request'].method == 'GET':
            self.fields['institution'] = InstitutionSerializer()

    institution = InstitutionSerializer() # remove this

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

    def update(self, instance, validated_data):
        institution_data = validated_data['institution']
        instance.institution = Institution.objects.get(pk=institution_data['id'])
        return instance

    class Meta:
        model = User
        fields = (
            'id',
            'username',
            'first_name',
            'last_name',
            'email',
            'password',
            'is_active',
            'institution',
        )
        datatables_always_serialize = (
            'id',
            'username',
            'first_name',
            'last_name',
            'email',
            'is_active',
            'institution',
        )

更新-1

如下更改 UserViewSet 类的 partial_update() 方法,

def partial_update(self, request, *args, **kwargs):
    user = User.objects.get(pk=request.data['id'])
    serializer = UserSerializer(instance=user, data=request.data, partial=True, context={"request": request}) # change is here &lt&lt&lt
    if serializer.is_valid():

        if 'password' in serializer.validated_data:
            serializer.validated_data['password'] = make_password(serializer.validated_data['password'])

        serializer.update(user, serializer.validated_data)
        return Response(serializer.data)
    else:
        return Response(serializer.errors, status.HTTP_500_INTERNAL_SERVER_ERROR)

【讨论】:

  • 返回错误:if self.context['request'].method == 'GET': KeyError: 'request'
  • 构造函数中没有请求
  • 有些变化,我现在有一个响应 {"institution":["Invalid hyperlink - No URL match."]}
  • 我已经解释了为什么validated_data 不包含institution 部分
  • 以适当的方式覆盖 __init__() 方法是另一种解决方案。我不确定为什么我的解决方案不起作用。
【解决方案2】:

我找到了一种方法来解决我的问题,使用 JPG 的想法。我只是使用 PrimaryKeyRelatedField 添加了一个 else 以允许序列化程序从 id 获取模型的引用。 可能还有另一种解决方案,但这个解决方案比多个序列化程序更有效且看起来更好。

class UserSerializer(serializers.HyperlinkedModelSerializer):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if self.context['request'].method == 'GET':
            self.fields['institution'] = InstitutionSerializer()
        else:
            self.fields['institution'] = serializers.PrimaryKeyRelatedField(queryset=Institution.objects.all())

【讨论】:

    猜你喜欢
    • 2016-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-20
    • 1970-01-01
    • 2023-01-10
    • 2017-01-28
    • 1970-01-01
    相关资源
    最近更新 更多