使用 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