【问题标题】:Creating instances of 2 related models using nested serializer in Django在 Django 中使用嵌套序列化程序创建 2 个相关模型的实例
【发布时间】:2020-12-18 10:20:59
【问题描述】:

我是 Django 的新手,我的代码遇到了这个问题。 我有一个自定义用户模型和一个帐户模型,它们由多对多字段关联。

在注册期间,系统会要求用户创建或不创建帐户(通过链接加入其他帐户)。

  • 如果用户创建了一个帐户,那么他就是该帐户的所有者,其他用户可以加入该帐户。(Did not finish the code for ownership)
  • 一个用户可以同时属于多个帐户。
  • 在注册视图中创建(或不创建)帐户和用户。

我在文档中阅读了有关嵌套序列化程序的信息,我认为这应该创建两个模型实例。 如何使用嵌套序列化程序在一个视图中创建关系? 解决问题的其他方法?

模型

class Account(models.Model):
    AccountName = models.TextField(max_length=100, blank=False, null=False)
class User(AbstractBaseUser):
    AccountName = models.ManyToManyField(Account)
    CreateAccount = models.BooleanField(blank=False, null=False)
    EmailId = models.EmailField(max_length=128, blank=False, null=False, unique=True)

    is_admin = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    is_superuser = models.BooleanField(default=False)

    USERNAME_FIELD = 'EmailId'
    REQUIRED_FIELDS = ['AccountName', 'CreateAccount',]

    # Implemented the other req. functions

    objects = MyAccountManager()

序列化器

class AccountCreationSerializer(ModelSerializer):
    class Meta:
        model = Account
        fields = ['AccountName']
class SignUpSerializer(ModelSerializer):

    AccountName = AccountCreationSerializer()

    class Meta:
        model = User
        fields = ['EmailId', 'AccountName', 'CreateAccount', 'password']
        extra_kwargs = {'password': {'write_only': True, 'required': True}}

    def create(self, validated_data):
        AccountName = validated_data.pop('AccountName')

        if validated_data['CreateAccount']: #Create only when this is True
            Account.objects.create(AccountName=AccountName, **AccountName)

        userAcc = User.objects.create_user(**validated_data)

        return userAcc

查看

class SignUpView(APIView):

    def post(request):
        # to edit
        signup_serializer = SignUpSerializer(data=request.data)
        
        # rest of the view

请求 // 忽略引号

EmailID: xyz@gmail.com
AccountName: TestAcc
CreateAccount: False
Password: ****

错误: Direct assignment to the forward side of a many-to-many set is prohibited. Use AccountName.set() instead.

自定义模型中的 Create_user

    def create_user(self, EmailId, AccountName, CreateAccount, password):
        if not EmailId:
            raise ValueError("Users must have an email")

        user = self.model(
            EmailId=self.normalize_email(EmailId),
            AccountName=AccountName,
            CreateAccount=CreateAccount,
        )

        user.set_password(password)
        user.save(using=self._db)
        return user

我很确定我在多线程领域犯了一些错误,但还没有找到解决方案。任何帮助都会对我有益。蒂亚!

【问题讨论】:

标签: python django django-rest-framework many-to-many


【解决方案1】:

您不能将值直接保存到many-to-many 字段。数据库不允许您这样做。它只允许您添加它们以关联两个表之间的关系( i.e User, Account )。将 Serializer 文件的代码段替换为以下代码段。

class AccountCreationSerializer(ModelSerializer):
    class Meta:
        model = Account
        fields = ['AccountName']


class SignUpSerializer(ModelSerializer):
    AccountName = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ['EmailId', 'AccountName', 'CreateAccount', 'password']
        extra_kwargs = {'password': {'write_only': True, 'required': True}}

    def validate(self, attrs):
        attrs = super(SignUpSerializer, self).validate(attrs=attrs)
        attrs.update({"AccountName": self.initial_data.get("AccountName")})
        return attrs

    def create(self, validated_data):
        AccountName = validated_data.pop('AccountName')
        acc = Account.objects.create(AccountName=AccountName) if "CreateAccount" in validated_data and validated_data['CreateAccount'] else None

        userAcc = User.objects.create_user(**validated_data)
        if acc:
            userAcc.AccountName.add(acc)

        return userAcc

最后,用以下方式替换你的SignUpView 类:

class SignUpView(APIView):
    serializer_class = SignUpSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        is_valid_serializer = serializer.is_valid(raise_exception=True)
        if is_valid_serializer:
            with transaction.atomic():
                serializer.save()
        # Rest of your code segments to finalize the response

更新

您的create_user 方法有问题。您在这里传递了 many-to-many 字段引用 (AccountName),这是不应该的。正如我前面提到的,您不能直接保存many-to-many 字段。您只需要关联它们之间的关系。省略它,它会工作!!!

遵循此方法的新定义 (create_user)。

    def create_user(self, EmailId, CreateAccount, password):
        if not EmailId:
            raise ValueError("Users must have an email")

        user = self.model(EmailId=self.normalize_email(EmailId), CreateAccount=CreateAccount)

        user.set_password(password)
        user.save(using=self._db)
        return user

【讨论】:

  • 我添加了代码,但它的行为仍然相同。我需要动态添加 Account 和 User 的实例,并在同一运行时关联它们的关系,我认为这不会将它们绑定在一起。我正在创建一个超级用户,并在提供 Account 和 CreateAccount = True 的详细信息后,它仍然给我同样的错误。
  • @otaku_weeb,我已经编辑了答案。检查。希望这次能帮到你。
  • 它仍然抛出同样的错误Direct assignment to the forward side of a many-to-many set is prohibited. Use AccountName.set() instead.
  • @otaku_weeb,我不这么认为。也许您没有像我在这里所说的那样完全遵循源代码。在我的源代码中,由于我已经正确处理了many-to-many 字段相关的情况,因此不应再次引发之前的错误。我在这里给出的那个解决方案已经使用我的机器进行了正确的测试。 API 工作正常。如果您觉得仍然不如预期,那么您需要针对这些方法(postcreate_user)和模型管理器(MyAccountManager)更新您的问题。
  • @otaku_weeb,请查看我回答的 UPDATE 部分。
猜你喜欢
  • 1970-01-01
  • 2023-03-08
  • 2020-05-23
  • 1970-01-01
  • 2016-03-01
  • 2018-06-23
  • 2015-12-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多