在 Django 的 Rest 框架中,如何关闭序列化器中某些字段的验证?

Dav*_*ave 7 django django-validation python-3.x django-serializer django-rest-framework

我正在使用 Django 3、Python 3.8 和 Django Rest 框架。此外,我正在使用这里的地址模块 - https://github.com/furious-luke/django-address。我已经为地址对象构建了以下序列化器以及我自己的序列化器之一依赖于它们......

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ['id', 'name', 'code']

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        return rep


class StateSerializer(serializers.ModelSerializer):
    country = CountrySerializer()
    class Meta:
        model = State
        fields = ['id', 'code', 'country']

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['country'] = CountrySerializer(instance.country).data
        return rep


class LocalitySerializer(serializers.ModelSerializer):
    state = StateSerializer()
    class Meta:
        model = Locality
        fields = ['id', 'name', 'postal_code', 'state']

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['state'] = StateSerializer(instance.state).data
        return rep

    def create(self, validated_data):
        """
        Create and return a new `Locality` instance, given the validated data.
        """
        validated_data['state'] = validated_data['state'].id
        print("\n\n\n\n****####\n\n", validated_data, "\n\n\n\n")
        return "{bogus}"
        #return Locality.objects.create(**validated_data)

class AddressSerializer(serializers.ModelSerializer):
    locality = LocalitySerializer()   #LocalityTypeField()

    class Meta:
        model = Address
        fields = ['id', 'street_number', 'route', 'raw', 'formatted', 'latitude', 'longitude', 'locality']

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['locality'] = LocalitySerializer(instance.locality).data
        return rep

    def create(self, validated_data):
        """
        Create and return a new `AddressField` instance, given the validated data.
        """
        address = AddressTypeField.objects.create(**validated_data)
        return address

class CoopSerializer(serializers.ModelSerializer):
    types = CoopTypeSerializer(many=True, allow_empty=False)
    addresses = AddressSerializer(many=True)   # AddressTypeField(many=True)
    phone = ContactMethodPhoneSerializer()
    email = ContactMethodEmailSerializer()

    class Meta:
        model = Coop
        fields = '__all__'

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        rep['types'] = CoopTypeSerializer(instance.types.all(), many=True).data
        rep['addresses'] = AddressSerializer(instance.addresses.all(), many=True).data
        return rep

    def create(self, validated_data):
        """
        Create and return a new `Snippet` instance, given the validated data.
        """

        coop_types = validated_data.pop('types', {})
        phone = validated_data.pop('phone', {})
        email = validated_data.pop('email', {})
        instance = super().create(validated_data)
        for item in coop_types:
            coop_type, _ = CoopType.objects.get_or_create(name=item['name'])
            instance.types.add(coop_type)
        instance.phone = ContactMethod.objects.create(type=ContactMethod.ContactTypes.PHONE, **phone)
        instance.email = ContactMethod.objects.create(type=ContactMethod.ContactTypes.EMAIL, **email)
        instance.save()
        return instance
Run Code Online (Sandbox Code Playgroud)

我正在尝试将数据传递到我的序列化器以进行保存......

@pytest.mark.django_db
def test_coop_create(self):
    """ Test coop serizlizer model """
    name = "Test 8899"
    coop_type_name = "Library"
    street = "222 W. Merchandise Mart Plaza, Suite 1212"
    city = "Chicago"
    postal_code = "60654"
    enabled = True
    postal_code = "60654"
    email = "test@example.com"
    phone = "7732441468"
    web_site = "http://www.1871.com"
    state = StateFactory()
    serializer_data = {
        "name": name,
        "types": [
            {"name": coop_type_name}
        ],
        "addresses": [{
            "raw": street,
            "formatted": street,
            "locality": {
                "name": city,
                "postal_code": postal_code,
                "state": {
                  "id": state.id,
                  "country": {
                    "id": state.country.id,
                    "name": state.country.name
                  }
                }
            }
        }],
        "enabled": enabled,
        "phone": {
          "phone": phone
        },
        "email": {
          "email": email
        },
        "web_site": web_site
    }

    serializer = CoopSerializer(data=serializer_data)
    assert serializer.is_valid(), serializer.errors
Run Code Online (Sandbox Code Playgroud)

但我不断收到验证错误...

    assert serializer.is_valid(), serializer.errors
AssertionError: {'addresses': [{'locality': {'state': {'country': {'name': [ErrorDetail(string='country with this name already exists.', code='unique')]}}}}]}
Run Code Online (Sandbox Code Playgroud)

如何关闭对某些我不想验证的字段的验证?具体来说,我不希望验证国家/地区名称字段。

编辑:为了响应答案,将我的 CountrySerializer 更改为

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ['id', 'name', 'code']
        extra_kwargs = {
            'name': {
                'validators': []
            }
        }

    def to_representation(self, instance):
        rep = super().to_representation(instance)
        return rep
Run Code Online (Sandbox Code Playgroud)

但得到了错误

======================================================================
ERROR: test_coop_create (tests.test_serializers.SerializerTests)
Test coop serizlizer model
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/davea/Documents/workspace/chicommons/maps/web/tests/test_serializers.py", line 95, in test_coop_create
    assert serializer.is_valid(), serializer.errors
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 234, in is_valid
    self._validated_data = self.run_validation(self.initial_data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 433, in run_validation
    value = self.to_internal_value(data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 490, in to_internal_value
    validated_value = field.run_validation(primitive_value)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 621, in run_validation
    value = self.to_internal_value(data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 657, in to_internal_value
    validated = self.child.run_validation(item)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 433, in run_validation
    value = self.to_internal_value(data)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 490, in to_internal_value
    validated_value = field.run_validation(primitive_value)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 435, in run_validation
    self.run_validators(value)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/serializers.py", line 468, in run_validators
    super().run_validators(to_validate)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/fields.py", line 588, in run_validators
    validator(value, self)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/validators.py", line 150, in __call__
    queryset = self.filter_queryset(attrs, queryset, serializer)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/validators.py", line 136, in filter_queryset
    return qs_filter(queryset, **filter_kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/rest_framework/validators.py", line 28, in qs_filter
    return queryset.filter(**kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/query.py", line 904, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/query.py", line 923, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1338, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1363, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1240, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg)
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 1078, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
  File "/Users/davea/Documents/workspace/chicommons/maps/web/venv/lib/python3.8/site-packages/django/db/models/sql/query.py", line 292, in get_meta
    return self.model._meta
AttributeError: 'NoneType' object has no attribute '_meta'
Run Code Online (Sandbox Code Playgroud)

GitHub 位于https://github.com/chicommons/maps/tree/master/web/directory

JPG*_*JPG 13

免责声明

name模型的- (源代码)字段似乎Country已设置为unique- (Django doc)条件,如果删除验证,数据库有可能会引发异常django.db.utils.IntegrityError: UNIQUE constraint failed

因此,您可以通过(至少)两种方式删除验证,

方法一

通过extra_kwargs-(DRF 文档)元类属性删除验证

class CountrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Country
        fields = ['id', 'name', 'code']
        extra_kwargs = {
            'name': {
                'validators': []
            }
        }
Run Code Online (Sandbox Code Playgroud)

方法2

name在序列化器中显式指定一个字段

class CountrySerializer(serializers.ModelSerializer):
    name = serializers.CharField()

    class Meta:
        model = Country
        fields = ['id', 'name', 'code']
Run Code Online (Sandbox Code Playgroud)


pyg*_*eek 1

问题

您并不孤单,这几年来一直是一个持续存在的问题(并且在文档中并不完全明显)。

您的模型序列化器设置为使用模型上找到的相同约束进行验证。对国家/地区模型名称列的独特约束似乎导致了此问题。

解决方案

据我所知,有 3 种方法可以解决此问题:

  1. 考虑删除模型中国家/地区字段的唯一约束。
  2. 使用自定义验证器来绕过验证。
  3. 使用 DRF Writable Nested,这增加了对此功能的支持。

参考:

唯一约束阻止嵌套序列化器更新:https://github.com/encode/django-rest-framework/issues/2996

处理嵌套序列化器中的唯一约束:https://medium.com/django-rest-framework/dealing-with-unique-constraints-in-nested-serializers-dade33b831d9

DRF 可写嵌套:https://github.com/beda-software/drf-writable-nested