使用 Django RF 创建具有多对多字段的对象的最佳方法 API
Best method to create an object with many to many field using Django RF API
我已经开始了一个项目,我的想法超出了我的理解能力,但是嘿......我们中的一些人学习的方式很愚蠢吧?
目前有 2 个模型:User
使用 AbstractUser
的模型 class 然后我有一个 Role
模型,它提供了许多可能随时间变化的角色,所以我需要它是动态的。
这是我的 api/models.py
的样子:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django import forms
class Role(models.Model):
'''
The Role entries are managed by the system,
automatically created via a Django data migration.
'''
CABLER = 1
CLIENT = 2
ENGINEER = 3
PROJECTMANAGER = 4
SALES = 5
PARTNER = 6
ADMIN = 6
ROLE_CHOICES = (
(CABLER, 'cabler'),
(CLIENT, 'client'),
(ENGINEER, 'engineer'),
(PROJECTMANAGER, 'project manager'),
(SALES, 'sales'),
(PARTNER, 'partner'),
(ADMIN, 'admin'),
)
id = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, primary_key=True)
def __str__(self):
return self.get_id_display()
class User(AbstractUser):
roles = models.ManyToManyField(Role, related_name='roles')
我 运行 遇到了几个问题,但目前当我使用邮递员或 DRF API 视图时,我无法创建有角色或没有角色的用户。
正在尝试角色:
Direct assignment to the forward side of a many-to-many set is prohibited. Use roles.set() instead.
无角色尝试:
{
"roles": [
"This list may not be empty."
]
}
现在,我知道我必须先创建用户,然后再分配角色,但是,我似乎无法通过 API 找到完成此操作的方法。
这是我的 UserSerializer
class:
class UserSerializer(serializers.ModelSerializer):
# roles = serializers.CharField(source='get_roles_display', )
# roles = RoleSerializer(many=True, read_only=False, )
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'roles')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
instance = User(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
我看到了创建 form.py
文件并添加保存方法的建议,但我不确定它是否适用,因为这将严格地作为我的后端 API 并将实施AngularJS 用于我的前端。
此外,每当我尝试在序列化程序下保存或更新用户时 User.roles.id
returns 用户没有名为角色的属性...
所以我希望有人能提供帮助。谢谢。
首先,与您的问题无关,您的角色 m2m 字段应更新为:
roles = models.ManyToManyField(Role, related_name='users')
related_name 是您如何从传递到 ManyToManyField(角色)的模型中引用当前模型(用户)。根据我的建议,你应该写 Role.objects.first().users
而不是 Role.objects.first().roles
.
其次,如果可能的话,我建议将角色作为主键列表传递给 create/update 用户。即使这意味着创建第二个仅用于写入的序列化程序 属性。例如:
class UserSerializer(serializers.ModelSerializer):
roles = RoleSerializer(many=True, read_only=True)
role_ids = serializers.PrimaryKeyRelatedField(
queryset=Role.objects.all(),
many=True, write_only=True)
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'roles', 'role_ids')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
# Allow the serializer to handle the creation. Don't do it yourself.
instance = super().create(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
当您的 API 吐出用户时,它应该看起来像:
{
'url': '...',
'username': '...',
'email': '...',
'roles': [{'id': 1, 'name': 'client'}],
}
当你 POST 时,你应该使用:
{
'url': '...',
'username': '...',
'email': '...',
'password': '...',
'role_ids': [1, 2],
}
顺便说一句,您收到错误 User does not have an attribute named roles.
的原因是您无法将 roles
传递给 User
的构造函数。 User.roles
需要在你有一个实例后设置。你应该做的:
user = User(**validated_data)
user.roles.set(roles) # If you need to re-write the entire list.
虽然超出了我的脑海,但我不确定它是否可行。您可能必须事先保存它。这就是为什么我建议您改用 super().create
。
我已经开始了一个项目,我的想法超出了我的理解能力,但是嘿......我们中的一些人学习的方式很愚蠢吧?
目前有 2 个模型:User
使用 AbstractUser
的模型 class 然后我有一个 Role
模型,它提供了许多可能随时间变化的角色,所以我需要它是动态的。
这是我的 api/models.py
的样子:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django import forms
class Role(models.Model):
'''
The Role entries are managed by the system,
automatically created via a Django data migration.
'''
CABLER = 1
CLIENT = 2
ENGINEER = 3
PROJECTMANAGER = 4
SALES = 5
PARTNER = 6
ADMIN = 6
ROLE_CHOICES = (
(CABLER, 'cabler'),
(CLIENT, 'client'),
(ENGINEER, 'engineer'),
(PROJECTMANAGER, 'project manager'),
(SALES, 'sales'),
(PARTNER, 'partner'),
(ADMIN, 'admin'),
)
id = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, primary_key=True)
def __str__(self):
return self.get_id_display()
class User(AbstractUser):
roles = models.ManyToManyField(Role, related_name='roles')
我 运行 遇到了几个问题,但目前当我使用邮递员或 DRF API 视图时,我无法创建有角色或没有角色的用户。
正在尝试角色:
Direct assignment to the forward side of a many-to-many set is prohibited. Use roles.set() instead.
无角色尝试:
{
"roles": [
"This list may not be empty."
]
}
现在,我知道我必须先创建用户,然后再分配角色,但是,我似乎无法通过 API 找到完成此操作的方法。
这是我的 UserSerializer
class:
class UserSerializer(serializers.ModelSerializer):
# roles = serializers.CharField(source='get_roles_display', )
# roles = RoleSerializer(many=True, read_only=False, )
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'roles')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
instance = User(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
我看到了创建 form.py
文件并添加保存方法的建议,但我不确定它是否适用,因为这将严格地作为我的后端 API 并将实施AngularJS 用于我的前端。
此外,每当我尝试在序列化程序下保存或更新用户时 User.roles.id
returns 用户没有名为角色的属性...
所以我希望有人能提供帮助。谢谢。
首先,与您的问题无关,您的角色 m2m 字段应更新为:
roles = models.ManyToManyField(Role, related_name='users')
related_name 是您如何从传递到 ManyToManyField(角色)的模型中引用当前模型(用户)。根据我的建议,你应该写 Role.objects.first().users
而不是 Role.objects.first().roles
.
其次,如果可能的话,我建议将角色作为主键列表传递给 create/update 用户。即使这意味着创建第二个仅用于写入的序列化程序 属性。例如:
class UserSerializer(serializers.ModelSerializer):
roles = RoleSerializer(many=True, read_only=True)
role_ids = serializers.PrimaryKeyRelatedField(
queryset=Role.objects.all(),
many=True, write_only=True)
class Meta:
model = User
fields = ('url', 'username', 'password', 'email', 'roles', 'role_ids')
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
password = validated_data.pop('password')
# Allow the serializer to handle the creation. Don't do it yourself.
instance = super().create(**validated_data)
if password is not None:
instance.set_password(password)
instance.save()
return instance
当您的 API 吐出用户时,它应该看起来像:
{
'url': '...',
'username': '...',
'email': '...',
'roles': [{'id': 1, 'name': 'client'}],
}
当你 POST 时,你应该使用:
{
'url': '...',
'username': '...',
'email': '...',
'password': '...',
'role_ids': [1, 2],
}
顺便说一句,您收到错误 User does not have an attribute named roles.
的原因是您无法将 roles
传递给 User
的构造函数。 User.roles
需要在你有一个实例后设置。你应该做的:
user = User(**validated_data)
user.roles.set(roles) # If you need to re-write the entire list.
虽然超出了我的脑海,但我不确定它是否可行。您可能必须事先保存它。这就是为什么我建议您改用 super().create
。