【问题标题】:How to use add(), set() and clear() methods of ManyRelatedManager in Django ManyToManyField with a through class?如何在 Django ManyToManyField 中通过类使用 ManyRelatedManager 的 add()、set() 和 clear() 方法?
【发布时间】:2020-11-20 07:58:06
【问题描述】:

我的模型如下,

Class Bank(models.Model):
    customers = models.ManyToManyField(Customer,'banks', through='bank_customer')

Class Customer(models.Model):
    name = models.CharField(max_length=50)

Class Bank_customer(models.Model):
    customer = models.ForeignKey(Customer,on_delete=models.CASCADE)
    bank = models.ForeignKey(Bank,on_delete=models.CASCADE)
    city = models.ForeignKey(City,on_delete=models.CASCADE)
    
Class City(models.Model):
    name = models.CharField(max_length=100)   

如何将Customer 对象添加到Bank?以下不起作用

bank.customers.add(customer)

这里的bankcustomer 是它们类的保存实例。这样做违反了not_nullcity ForeignKey 在Bank_customer 表中的约束。

【问题讨论】:

标签: python django many-to-many manytomanyfield manyrelatedmanager


【解决方案1】:

首先,一些通用的cmets。我不是专家,但我会先对模型进行这些更改。

  • 注意保留字class,必须全小写。

  • 定义类时,顺序很重要。继承或向后相关的类必须是defined last。 (例如:Bank 必须在 Customer 之后定义,否则会出现 “未定义客户” 之类的错误。

  • 我认为您可能需要Bank 模型中的name 属性/字段。我的意思是,银行有自己的名字。

  • Bank 类中,我认为最好明确使用相关名称关键字:related_name='banks'。它使可读性更好。

  • 使用 CapWords 约定 (PEP 8) 命名类更符合 Pythonic。 使用BankCustomer 代替Bank_customer 模型。


关于您的具体问题。

我这样做了:

c1 = Customer(name="Guido van Rossum")
c1.save()
b1 = Bank(name="Bank of America")
b1.save()

然后我做了b1.customers.add(c1) 并引发了这个错误:IntegrityError: NOT NULL constraint failed: app2_bankcustomer.city_id。 它似乎期望city_idBankCustomer 中有null=True

我继续:

ct1 = City('New York')
ct1.save()
bc1 = BankCustomer(customer=c1, bank=b1, city=ct1)
bc1.save()

然后当我执行b1.customers.add(c1) 时,没有出现任何错误。

问题是IntegrityError 错误,显然:

django.db.utils.IntegrityError: NOT NULL --> 这是一个很常见的 初学者的错误。这意味着你有一个字段是 null=False(又名default)并且您正在尝试创建它 没有该字段的模型有任何数据。 (source)

因此您可以对BankCustomer 进行一些更改。您在city 字段中添加null=True

class BankCustomer(models.Model):
    customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
    bank = models.ForeignKey(Bank, on_delete=models.CASCADE)
    city = models.ForeignKey(City, null=True, on_delete=models.CASCADE)

    def __str__(self):
        return f'{self.customer} - {self.bank} - {self.city}'

别忘了运行 makemigrations 和迁移

在此之后,不会引发任何错误。

讨论该问题的其他一些来源:

django.db.utils.IntegrityError: NOT NULL constraint failed: app.area_id

django.db.utils.IntegrityError: NOT NULL constraint failed: products_product.image ERROR WITH IMAGE FIELD

https://code.djangoproject.com/ticket/21783

https://forum.djangoproject.com/t/polls-not-null-constraint-failed/4396/2

https://github.com/sibtc/django-beginners-guide/issues/20


如果您查看管理员,您会看到 Bank 模型只接受 name 而不接受 customers

我认为这是合乎逻辑的,因为您想使用 BankCustomer(在您的代码中:Bank_customer)。

我希望这会有所帮助。

【讨论】:

    【解决方案2】:

    我找不到它的 django 文档。但是查看ManyRelatedManager__add的Django3.1源代码,它接受through_defaults参数

    def add(self, *objs, through_defaults=None):
    ...
                    self._add_items(
                        self.source_field_name, self.target_field_name, *objs,
                        through_defaults=through_defaults,
                    )
    ...
    

    所以,而不是

    bank.customers.add(customer)
    

    试试

    bank.customers.add(customer, through_defaults={'city_id':SOME_CITY_OBJ_ID_YOU_WANT})
    

    这也适用于.set() 函数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-12-11
      • 1970-01-01
      • 2020-08-09
      相关资源
      最近更新 更多