Django rest框架未使用FK创建对象到具有unique = True字段的模型 [英] Django rest framework not creating object with FK to a model with unique=True field

查看:80
本文介绍了Django rest框架未使用FK创建对象到具有unique = True字段的模型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个这样的模型:

class Sector(models.Model):
    name = models.CharField(max_length=100, db_index=True, unique=True)  # HERE IF I REMOVE unique=True, it works correctly

class Address(models.Model):
    ...
    sector = models.ForeignKey(Sector, null=True, blank=True)

地址模型的序列化器:

在视图中,我有以下内容:

In the view, I have this:

    address_serialized = AddressSerializer(data=request.data)
    if address_serialized.is_valid():
        address_serialized.save(client=client)

它永远不会进入 create 函数。我有一个使用create函数进行序列化的代码,如下所示:

It never gets to the create function. I have a serialized with a create function that looks like this:

class AddressSerializer(serializers.ModelSerializer):
    city_gps = CitySerializer(required=False)
    sector = SectorSerializer(required=False)

    class Meta:
        model = Address
        fields = (..., "sector")

    def create(self, validated_data):
        ...
        sector_dict = validated_data.get("sector", None)   
        sector = None

        if sector_dict and "name" in sector_dict and city_gps:
            if Sector.objects.filter(name=sector_dict["name"], city=city_gps).exists():
                sector = Sector.objects.get(name=sector_dict["name"], city=city_gps)

        # pdb.set_trace()
        if "sector" in validated_data:
            validated_data.pop("sector")
        if "city_gps" in validated_data:
            validated_data.pop("city_gps")

        address = Address.objects.create(sector=sector, city_gps=city_gps, **validated_data)

        return address

代码从不触及此函数, is_valid()返回False。消息是

The code never touches this function, is_valid() returns False. And the message is


{ sector:{ name:[具有该名称的扇区已经存在。]}}

{"sector":{"name":["sector with this name already exists."]}}

我需要能够使用FK为现有部门创建一个新地址。我该如何实现?任何建议都会有所帮助。

I need to be able to create a new address with FK to the already existing sector. How can I achieve that? Any advice will help.

编辑

视图如下所示:

class ClientProfileAddressCreateView(APIView):
    # throttle_scope = '1persecond'
    renderer_classes = (JSONRenderer,)
    permission_classes = (IsAuthenticated,)

    def post(self, request):

        try:
            client = Client.objects.get(user=request.user)
        except ObjectDoesNotExist:
            return Response({"error": "A client profile for the logged user does not exit"},
                            status=status.HTTP_404_NOT_FOUND)

        address_serialized = AddressSerializer(data=request.data)
        print("address_serialized.is_valid: %s" % address_serialized.is_valid())  # Returns False when unique=True in models
        if address_serialized.is_valid():
            # print("address_serialized: %s" % address_serialized.data)
            address_serialized.save(client=client)
        else:
            return Response(data=address_serialized.errors, status=status.HTTP_400_BAD_REQUEST)

        return Response(data=address_serialized.data, status=status.HTTP_201_CREATED)


推荐答案

这是一个已知问题具有嵌套的序列化程序和独特的约束。

This is a known issue with nested serializers and unique constraints.

真正要做的事情实际上是打印序列化程序-这样可以给您很多额外的信息。

Really awesome thing to always do is actually print the Serializer - that can give you a lot of extra info.

当您拥有这样的json:

When you have a json like this:

{
    "Sector": {
        "name": "Sector XYZ"
    },
    "address_line_one": "Some Random Address"
}

Django REST框架不知道您是要创建还是获取Sector对象,因此它会强制对每个请求进行验证。

Django REST framework does not know whether you're creating or getting the Sector object, thus it forces validation on every request.

您需要执行的操作如下:

What you need to do is the following:

class SectorSerializer(serializers.ModelSerializer):
    # Your fields.
    class Meta:
        model = Address
        fields = ("Your Fields",)

        extra_kwargs = {
            'name': {
                'validators': [],
            }
        }

然后处理验证,您将需要重做创建/更新部分以适应唯一性约束并引发异常/验证错误。

Then to handle validation you would need to redo the create/update part to fit the uniqueness constraint and raise exception/validation error.

我希望这会有所帮助。

I hope this helps.

有用的链接:此SO答案在嵌套序列化程序中使用唯一约束进行处理

编辑:

根据cezar的要求:我将添加重写序列化器的 create 方法的外观。我没有尝试过这段代码,但是逻辑是这样的。

As per cezar's request: I will add how it might look like to override the create method of the serializer. I have not tried this code, but the logic goes like this.

class SectorSerializer(serializers.ModelSerializer):
    # Your fields.
    class Meta:
        model = Address
        fields = ("Your Fields",)

        extra_kwargs = {
            'name': {
                'validators': [],
            }
        }

    def create(self, validated_data):
        raise_errors_on_nested_writes('create', self, validated_data)

        ModelClass = self.Meta.model

        info = model_meta.get_field_info(ModelClass)
        many_to_many = {}
        for field_name, relation_info in info.relations.items():
            if relation_info.to_many and (field_name in validated_data):
                many_to_many[field_name] = validated_data.pop(field_name)

        # FIELD CHECK
        your_field = validated_data.get("your_field","") # or validated_data["your_field"] 
        try:
            YourModel.objects.filter(your_check=your_field).get()
            raise ValidationError("Your error")
        except YourModel.DoesNotExist:
            # if it doesn't exist it means that no model containing that field exists so pass it. You can use YourQuerySet.exists() but then the logic changes
            pass

        try:
            instance = ModelClass.objects.create(**validated_data)
        except TypeError:
            tb = traceback.format_exc()
            msg = (
                'Got a `TypeError` when calling `%s.objects.create()`. '
                'This may be because you have a writable field on the '
                'serializer class that is not a valid argument to '
                '`%s.objects.create()`. You may need to make the field '
                'read-only, or override the %s.create() method to handle '
                'this correctly.\nOriginal exception was:\n %s' %
                (
                    ModelClass.__name__,
                    ModelClass.__name__,
                    self.__class__.__name__,
                    tb
                )
            )
            raise TypeError(msg)

        # Save many-to-many relationships after the instance is created.
        if many_to_many:
            for field_name, value in many_to_many.items():
                field = getattr(instance, field_name)
                field.set(value)

        return instance

这篇关于Django rest框架未使用FK创建对象到具有unique = True字段的模型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆