Как создать пользователя во вложенном сериализаторе, используя общий ListCreateAPIView?

#django #django-rest-framework #django-views #django-serializer

#django #django-rest-framework #django-просмотры #django-сериализатор

Вопрос:

Я работаю над genericAPIViews в DRF. Я использую встроенную пользовательскую модель с моделью UserProfile, имеющей отношение один к одному с ней. Но я не могу создать пользователя из-за вложенного сериализатора. Мой вопрос заключается в том, как я могу создать свою встроенную модель пользователя и модель профиля пользователя одновременно с тем, как модель UserProfile вложена в модель пользователя.Вот мой код:

Models.py

 USER_CHOICE = (
    ('SS', 'SS'),
    ('SP', 'SP')
)
LOGIN_TYPE = (
    ('Local', 'Local'),
    ('Facebook', 'Facebook'),
    ('Google', 'Google')
)


class UserProfile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    cell_phone = models.CharField(max_length=15, blank=True, default="", null=True)
    country = models.CharField(max_length=50, blank=True, default="", null=True)
    state = models.CharField(max_length=50, blank=True, default="", null=True)
    profile_image = models.FileField(upload_to='user_images/', default='', blank=True)
    postal_code = models.CharField(max_length=50, blank=True, default="", null=True)
    registration_id = models.CharField(max_length=200, null=True, blank=True, default=None)
    active = models.BooleanField(default=True)
    # roles = models.ForeignKey(Role, null=True, on_delete=models.CASCADE, related_name='role', blank=True)
    user_type = models.CharField(max_length=50, choices=USER_CHOICE, null=True, blank=True)
    login_type = models.CharField(max_length=40, choices=LOGIN_TYPE, default='local')
    reset_pass = models.BooleanField(default=False)
    confirmed_email = models.BooleanField(default=False)
    remember_me = models.BooleanField(default=False)
    reset_code = models.CharField(max_length=200, null=True, blank=True, default="")
    reset_code_time = models.DateTimeField(auto_now_add=True, blank=True)
    longitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
    latitude = models.DecimalField(max_digits=80, decimal_places=10, default=0.00)
    r_code = models.CharField(max_length=15, null=True, blank=True)
    refer_user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True, related_name="user_refer")
    referred = models.ManyToManyField(User, related_name="user_referred", null=True, blank=True)
    otp = models.CharField(max_length=6, blank=True, default="", null=True)

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

Seralizers.py

 from rest_framework import serializers
from django.contrib.auth.models import User
from .models import UserProfile


class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model = UserProfile
        fields = '__all__'


class UserSerializer(serializers.ModelSerializer):
    profile = UserProfileSerializer()

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
 

Views.py

 class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer



class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = [IsAdminUser]
 

Я знаю, что есть метод .create(), который может быть переопределен в соответствии с документацией DRF. Кроме того, я хочу переопределить этот метод в views.py . Есть ли какой-нибудь подход к этому? Заранее благодарю за ваше дополнение в моих знаниях.

Комментарии:

1. Позвольте мне прояснить ситуацию: вы создали представление для создания User и теперь ищете способ, чтобы при User создании an UserProfile также создавался для этого пользователя, верно?

2. Вот именно! но с использованием genericAPIviews. Мой APIView работает нормально, когда я создаю только пользователя, но проблема возникает, когда я вкладываю UserProfile serializer в пользовательскую сборку в модели.

Ответ №1:

Насколько мне известно, есть два способа сделать это.

Первый переопределяет метод create общего представления. Который заключается в следующем:

 class UserList(generics.ListCreateAPIView):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    def create(self, request, *args, **kwargs):

        #let django create the user with generic view
        response = super().create(request, *args, **kwargs)

        # response.data contains the serialized data of the created user
        user_data = response.data
        user_id = response.data.get("id")

        profile = UserProfile()
        profile.user = User.objects.get(id=user_id)
        profile.save()
        
        # return the response created by the generic view
        return response
 

Будьте осторожны, что Django Rest Framework не поддерживает create метод для вложенных сериализаторов. Установите для поля профиля значение только для чтения в UserSerializer

 class UserSerializer(serializers.ModelSerializer):
    profile = SerializerMethodField()

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'first_name', 'last_name', 'profile']
        read_only_fields = ('profile',)

    def get_profile(self, user):
        profile = UserProfile.objects.filter(user=user).first()
        return UserProfileSerializer(profile).data if profile is not None else None
 

Второй способ — использование сигналов. Signals — это диспетчер событий, который отслеживает такие события, как обновление, удаление, изменение m2m и т. Д. Здесь мы можем использовать post_save сигналы, которые запускаются всякий раз, когда модель обновляется. Итак, если User модель будет создана, мы получим сигнал и, следовательно, создадим ящик a UserProfile для этого пользователя. Вы можете узнать больше о сигналах на странице сигналов Django.

 @receiver(post_save, sender=User, dispatch_uid="create_user_profile")
def on_user_create(sender, instance, created, **kwargs):
    
    # here instance is the user object that just got created
    # created is a boolean value which will be true if the record is newly created
    if created:
        
        profile = UserProfile()
        profile.user = instance
        profile.save()
 

Комментарии:

1. мой код выходит из строя здесь: response = super().create(запрос, * аргументы, ** kwargs)

2. По .create() умолчанию метод не поддерживает доступные для записи вложенные поля. Напишите явный .create() метод для сериализатора myapp.serializers.UserSerializer или установите read_only=True для вложенных полей сериализатора.

3. удалите profile поле из UserSerializer . DRF не поддерживает create метод для вложенных сериализаторов.

4. что ж, спасибо за помощь. Но я знаю это и пытался выяснить, есть ли какой-либо метод для переопределения метода create() и выполнения этой задачи.

5. Я обновил свой фрагмент. Пожалуйста, попробуйте это сейчас.