【问题标题】:How to Create a User in nested serializer using generic ListCreateAPIView?如何使用通用 ListCreateAPIView 在嵌套序列化程序中创建用户?
【发布时间】:2021-05-21 18:06:36
【问题描述】:

我正在研究 DRF 中的 genericAPIViews。我正在使用与 UserProfile 模型具有一对一关系的内置用户模型。但是由于嵌套的序列化程序,我无法创建用户。我的问题是如何在 UserProfile 模型嵌套在 User 模型中的同时创建我的内置用户模型和 Profile User 模型。这是我的代码:

模型.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

序列化器.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]

我知道根据 DRF 文档可以覆盖 .create() 方法。另外我想在views.py中覆盖这个方法。有什么方法可以做到这一点。提前感谢您的补充。

【问题讨论】:

  • 让我直截了当地说,您已经创建了一个创建User 的视图,现在正在寻找一种方法,以便在创建User 时,也会为该用户创建一个UserProfile对吧?
  • 没错!但使用genericAPIviews。当我只创建一个用户时,我的 APIview 工作正常,但是当我在用户构建模型中嵌套 UserProfile 序列化程序时出现问题。

标签: django django-rest-framework django-views django-serializer


【解决方案1】:

据我所知有两种方法。

第一个是覆盖通用视图的创建方法。如下:

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 模型,我们将收到一个信号,因此为该用户创建一个 UserProfile。您可以从Django Signals 页面了解有关信号的更多信息。

@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()

【讨论】:

  • 我的代码在这里崩溃:response = super().create(request, *args, **kwargs)
  • .create() 方法默认不支持可写嵌套字段。为序列化器myapp.serializers.UserSerializer 写一个显式的.create() 方法,或者在嵌套的序列化器字段上设置read_only=True
  • UserSerializer 中删除 profile 字段。 DRF 不支持嵌套序列化程序的 create 方法。
  • 非常感谢您的帮助。但是我知道这一点并且正在尝试是否有任何方法可以覆盖 create() 方法并执行此任务。
  • 我已经更新了我的 sn-p。请立即尝试。
猜你喜欢
  • 1970-01-01
  • 2016-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多