DRF: как изменить значение полей модели перед сохранением в базе данных - PullRequest
3 голосов
/ 11 марта 2020

Если мне нужно изменить некоторые значения полей перед сохранением в базу данных, так как я думаю, что метод моделей clear() подходит. Но я не могу позвонить ему, несмотря на все мои усилия.

Например, поля электронная почта Мне нужно установить строчные буквы и поля и Мне нужно устанавливается как null

models.py

class Vendors(models.Model):

    nda = models.DateField(blank=True, null=True)
    parent = models.OneToOneField('Vendors', models.DO_NOTHING, blank=True, null=True)

    def clean(self):
        if self.nda == "":
            self.nda = None

class VendorContacts(models.Model):
    ....
    vendor = models.ForeignKey('Vendors', related_name='contacts', on_delete=models.CASCADE)
    email = models.CharField(max_length=80, blank=True, null=True, unique=True)

    def clean(self):
        if self.email:
            self.email = self.email.lower()

serializer.py

class VendorContactSerializer(serializers.ModelSerializer):
    class Meta:
        model = VendorContacts
        fields = (
                  ...
                  'email',)

class VendorsSerializer(serializers.ModelSerializer):
    contacts = VendorContactSerializer(many=True)

    class Meta:
        model = Vendors
        fields = (...
                  'nda',
                  'contacts',
                  )

    def create(self, validated_data):
        contact_data = validated_data.pop('contacts')
        vendor = Vendors.objects.create(**validated_data)
        for data in contact_data:
            VendorContacts.objects.create(vendor=vendor, **data)

        return vendor

views.py

class VendorsCreateView(APIView):
    """Create new vendor instances from form"""
    permission_classes = (permissions.AllowAny,)
    serializer_class = VendorsSerializer

    def post(self, request, *args, **kwargs):
        serializer = VendorsSerializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            serializer.save()
        except ValidationError:
            return Response({"errors": (serializer.errors,)},
                            status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(request.data, status=status.HTTP_200_OK)

Как я узнал из документации

Django Сериализаторы Rest Framework не вызывают Model.clean при проверке сериализаторов моделей

Решая эту проблему, я нашел два способа ее решения. 1. используя пользовательский метод в сериализаторе. Для моего случая это выглядит как

класс VendorsSerializer (serializers.ModelSerializer): contacts = VendorContactSerializer (many = True)

class Meta:
    model = Vendors
    fields = (...
              'nda',
              'contacts',
              )

def create(self, validated_data):
    contact_data = validated_data.pop('contacts')
    vendor = Vendors.objects.create(**validated_data)
    for data in contact_data:
        VendorContacts.objects.create(vendor=vendor, **data)

    return vendor

def validate(self, attrs):
    instance = Vendors(**attrs)
    instance.clean()
    return attrs
Используя метод full_clean(). Для меня это выглядит как

класс VendorsSerializer (serializers.ModelSerializer): contacts = VendorContactSerializer (many = True)

class Meta:
    model = Vendors
    fields = (...
              'nda',
              'contacts',
              )

def create(self, validated_data):
    contact_data = validated_data.pop('contacts')
    vendor = Vendors(**validated_data)
    vendor.full_clean()
    vendor.save()
    for data in contact_data:
        VendorContacts.objects.create(vendor=vendor, **data)

    return vendor

Но в обоих случаях метод clean () не называется. Я действительно не понимаю, что я делаю неправильно.

Ответы [ 3 ]

2 голосов
/ 11 марта 2020

Для DRF вы можете изменить свой сериализатор перед сохранением, как показано ниже ...

Прежде всего, вы должны убедиться, что serializer является действительным или нет, а если это valid, то измените требуемый объект serializer и затем сохраните это serializer.

if serializer.is_valid():
    serializer.object.user_id = 15  # For example 
    serializer.save() 

UPD! views.py

class VendorsCreateView(APIView):
    """Create new vendor instances from form"""
    permission_classes = (permissions.AllowAny,)
    serializer_class = VendorsSerializer

    def post(self, request, *args, **kwargs):
        data = request.data
        if data['nda'] == '':
            data['nda'] = None
        for contact in data['contacts']:
            if contact['email']:
                print(contact['email'])
                contact['email'] = contact['email'].lower()
        serializer = VendorsSerializer(data=request.data)
        try:
            serializer.is_valid(raise_exception=True)
            serializer.save()
        except ValidationError:
            return Response({"errors": (serializer.errors,)},
                            status=status.HTTP_400_BAD_REQUEST)
1 голос
/ 11 марта 2020

Чтобы ответить на ваш вопрос: просто переопределите save() метод для ваших моделей , как написано в документах . Там вы можете назначить любые значения экземпляру вашей модели непосредственно перед сохранением его в базе данных.

Кроме того, вам, вероятно, следует использовать models.EmailField для полей электронной почты, которые избавят вас от проверки нижнего ().

0 голосов
/ 12 марта 2020

В моем случае у меня была та же проблема, но с функцией проверки я использовал способ, описанный ниже, и он работает для меня (не исключает способ, описанный выше):

class CustomViewClass(APIView):

    def post(self, request, format=None):

        prepared_data_variable = 'some data in needed format'
        serializer = CustomSerializer(data=request.data)

        if serializer.is_valid(self):
            serializer.validated_data['field_name'] = prepared_data_variable
            serializer.save()
            return Response(data=serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Эта строка является ключевой для моего решения serializer.validated_data ['field_name'] = prepare_data_variable

...