Django сериализатор с полем, имеющим отношение OneToOne - PullRequest
0 голосов
/ 04 апреля 2020

В моем проекте у меня есть два «типа» пользователей: клиенты и предприятия. Они являются расширением django базового пользователя от django.contrib.auth.models.User.

У меня есть в моем models.py:

class Customer(models.Model):
    user = models.OneToOneField(User, related_name='user', on_delete=models.CASCADE)

    birth_date = models.DateField(blank=True, null=True)
    phone = PhoneNumberField(unique=True)

    def __str__(self):
        return self.user.username

class Business(models.Model):
    user = models.OneToOneField(User, related_name='business', on_delete=models.CASCADE)

    cf = models.CharField(max_length=16, validators=[ssn_validation])
    birth_date = models.DateField(null=True)
    city = models.CharField(max_length=50, blank=False)
    address = models.CharField(max_length=150, blank=False)

Хорошо, тогда у меня есть две разные регистрации, одна для Клиенты и один для бизнеса. Проблема в том, что для проверки пароля, отправленного из REST API, мне нужно сравнить password с password2, создать пользователя (django base) и передать его в мой Customer.objects.create, например: :

У меня в serializers.py:

class CustomerRegistationSerializer(serializers.ModelSerializer):
    username = serializers.CharField(source='user.username',
                                     validators=[UniqueValidator(queryset=User.objects.all())])
    email = serializers.CharField(source='user.email',
                                  validators=[UniqueValidator(queryset=User.objects.all())])
    first_name = serializers.CharField(source='user.first_name')
    last_name = serializers.CharField(source='user.last_name')
    password = serializers.CharField(source='user.password', write_only=True)
    password2 = serializers.CharField(style={'input_style': 'password'}, write_only=True)

    birth_date = serializers.CharField(required=False)

    class Meta:
        model = Customer
        fields = ['id', 'username', 'email', 'password', 'password2', 'first_name', 'last_name',
                  'birth_date', 'phone']

    def save(self):
        username = self.validated_data['user']['username']
        password = self.validated_data['user']['password']
        password2 = self.validated_data['password2']
        email = self.validated_data['user']['email']
        first_name = self.validated_data['user']['first_name']
        last_name = self.validated_data['user']['last_name']
        phone = self.validated_data['phone']

        try:
            birth_date = self.validated_data['birth_date']
        except KeyError:
            birth_date = None

         if password != password2:
            raise serializers.ValidationError({'password': 'Passwords must match!'})

        user = User.objects.create(username=username, email=email, first_name=first_name, last_name=last_name)
        user.set_password(password)
        user.is_active = False
        user.save()

        customer = Customer.objects.create(user=user,
                                           birth_date=birth_date,
                                           phone=phone)
        return customer

Это на самом деле работает, но в случае ошибок может произойти, что пользователь создан, а клиент - нет. Есть ли более чистый способ регистрации клиентов, всегда проверяющий password == password2?

РЕДАКТИРОВАТЬ: Я нашел более элегантный способ справиться с этим:

@ транзакция. atomic def save (self): password = self.validated_data ['user'] ['password'] password2 = self.validated_data ['password2']

user = User.objects.create(**self.validated_data['user'])

if password != password2:
    raise serializers.ValidationError({'password': 'Passwords must match!'})
user.set_password(password)
user.is_active = False
user.save()
update_last_login(None, user)

del self.validated_data['user']
del self.validated_data['password2']

customer = Customer.objects.create(user=user, **self.validated_data)
return customer

1 Ответ

0 голосов
/ 04 апреля 2020

Если вы хотите требовать, чтобы все транзакции БД, которые вы выполняете во время метода save(), были успешными, чтобы эффективно записать его в БД и ничего не записывать, если в какой-то момент процесса возникла ошибка, вы обычно запрашивает атомарность (одну из четырех возможностей ACID базы данных)

Используйте этот Django декоратор, обычно для этого:

from django.db import transaction

    @transaction.atomic    
    def save(self):
        <...>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...