如何在 django rest_framework 的 1 个视图中同时创建 2 个不同的对象?DRF.



我想做的是,同时创建user对象和staffprofile对象。username将基于first_name字段和last_name字段,该字段将基于随之创建的staffprofile对象。并且password将使用随机模块自动生成。

我有一个自定义User模型,它继承了AbstractUser并具有以下参数:

class User(AbstractUser):
is_student = models.BooleanField(default = False, verbose_name = 'Student')
is_staff = models.BooleanField(default = False, verbose_name= 'Staff')
is_teacher = models.BooleanField(default = False, verbose_name='Teacher')
is_superuser = models.BooleanField(default = False, verbose_name= 'Administrator')
is_registrar = models.BooleanField (default = False, verbose_name= 'Registrar')
email = models.EmailField(
max_length=254,
unique=True,
verbose_name='Email Address',
blank=True,
null=True,
)
def __str__(self):
return self.username

我有一个StaffProfile模型,它通过user属性与User模型相关:

class StaffProfile(models.Model):
GENDER_CHOICES = [
('Male', 'Male'),
('Female', 'Female'),
]
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
primary_key= True,
limit_choices_to= Q(is_staff= True),
related_name= 'staffprofile',
)
first_name = models.CharField(max_length= 70,)
middle_name = models.CharField(max_length=70,)
last_name = models.CharField(max_length=70,)
gender = models.CharField(
max_length= 6,
choices=GENDER_CHOICES,
default='Male',
)
employee_number= models.CharField(max_length= 15)
date_of_birth = models.DateField()
mobile_number = models.CharField(max_length= 15)
address= models.TextField()
def get_full_name(self):
"""
Return the first_name plus the last_name, with a space in between.
"""
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def __str__(self):
full_name = self.get_full_name()
return full_name.strip()

像这样 views.py 可以做到吗? (我还没有写下来,因为我还不确定。我知道这是错误的,我也想知道我的想法是否也是错误的,或者我是否应该在 serializers.py 而不是 views.py 上做逻辑。或者也许使用serializers.Serializer而不是serialiazers.ModelSerializer.)

@api_view(['POST'])
def createuserandprofile(request):
userserializer = CreateUserSerializer(data= request.data)
profileserializer = CreateProfileSerializer(data= request.data)
if userserializer.is_valid() and profileserializer.is_valid():
validated_data = profileserializer.validated_data
firstname = validated_data.get('first_name')
lastname = validated_data.get('last_name')
#a function I wrote to concatenate first_name and last_name
username = generate_username(firstname, lastname)
#a function I wrote to generate random password
password = generate_password()
user = User.objects.create(username=username, password=password, and so on...)
user.save()
profileserializer.save(user= user)

#I am not sure what to return
return Response(...)

也许在 serializers.py 上做这样的事情:

class CreateUserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'

class CreateProfileSerializer(serializer.ModelSerializer):
class Meta:
model = StaffProfile
fields= '__all__'
depth = 1

你的解决方案并不可怕。不要害羞,特别是如果你刚刚开始。我建议查看通用视图,因为它们可以让您的生活更轻松。

我想向您展示,实现这一目标的解决方案不止一种,我认为它们中的任何一个都不能被标记为每个项目的最佳解决方案

解决方案 1.编写您自己的ModelSerializer.save

我建议您从阅读本文开始:https://www.django-rest-framework.org/api-guide/relations/#writable-nested-serializers。它演示了一个示例,如何在 DRF 中实现可写嵌套序列化程序。

解决方案 2.使用第三方软件包

使用裸 DRF,您将需要自己实现 create 方法,但也有一些包可以开箱即用地用于简单的嵌套序列化程序 (https://github.com/beda-software/drf-writable-nested)。

解决方案 3.使用普通serializers.Serializer

这是我只推荐用于ModelSerializer可能成为限制因素的大型应用程序的解决方案。一些企业级项目引入了服务层。您可以将服务视为封装部分业务逻辑的函数。调用这样的函数可以通过以下方式完成:user = create_user_service(**serializer.validated_data)

这样,可以独立于 REST API 测试业务逻辑。服务还可以调用其他服务,而无需在此过程中创建模型序列化程序。

此解决方案需要最大的工作量,因为您需要自己定义序列化程序字段,并确保它们与模型字段中强制实施的约束匹配。

结语

我建议您用transaction.atomic()(https://docs.djangoproject.com/en/3.1/topics/db/transactions/#controlling-transactions-explicitly)包装整个逻辑。它确保创建StaffUser对象时,要么两者都创建,要么不创建对象。

作为审阅者,我会选择最适合现有项目架构的解决方案,同时确保数据完整性。

最新更新