Reputation: 307
I have models as follows,
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)
How do I add Customer
objects to Bank
? The following does not work
bank.customers.add(customer)
Here bank
and customer
are saved instances of their classes. Doing this violates not_null
constraint for city
ForeignKey in Bank_customer
table.
Upvotes: 3
Views: 2197
Reputation: 3467
I would make this changes to the models to start.
Be carefull with the reserved word class
, it must be all lowercase.
When you define the classes, the order is important. The classes that inherit or are backward-related must be defined last. (eg: Bank
must be defined after Customer
, if not you'll have an error like "Customer is not defined" or something like that).
I think that you may need a name
attribute/field in the Bank
model. I mean, banks have a name.
In the Bank
class I think it's better to explicitly use the related name keyword: related_name='banks'
. It makes readability better.
It's more Pythonic to name classes using the CapWords convention (PEP 8).
Use BankCustomer
instead of Bank_customer
model.
About your specific question.
I did this:
c1 = Customer(name="Guido van Rossum")
c1.save()
b1 = Bank(name="Bank of America")
b1.save()
Then I did b1.customers.add(c1)
and this error was raised: IntegrityError: NOT NULL constraint failed: app2_bankcustomer.city_id
.
It seems that it was expecting city_id
to have null=True
in the BankCustomer
.
I continued with:
ct1 = City('New York')
ct1.save()
bc1 = BankCustomer(customer=c1, bank=b1, city=ct1)
bc1.save()
And then when I did b1.customers.add(c1)
no errors were raised.
The problem is the IntegrityError
error, apparently:
django.db.utils.IntegrityError: NOT NULL
--> this is a very common error for beginners. It means that you have a field that isnull=False
(aka thedefault
) and you're trying to create that model WITHOUT that field having any data. (source)
So you can make a little change to the BankCustomer
. You add null=True
in the city
field.
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}'
(Don't forget to run makemigrations and migrate)
After this, no errors are raised.
Some other sources that discuss the issue:
django.db.utils.IntegrityError: NOT NULL constraint failed: app.area_id
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
If you check the admin you'll see that the Bank
model only accepts name
and no customers
.
Which I think is logical because you want to use the BankCustomer
(in your code: Bank_customer
).
I hope this helps.
Upvotes: 3
Reputation: 410
I can't find the django doc for it. however looking at the Django3.1's source code of ManyRelatedManager__add
, it accepts through_defaults
parameter
def add(self, *objs, through_defaults=None):
...
self._add_items(
self.source_field_name, self.target_field_name, *objs,
through_defaults=through_defaults,
)
...
so, instead of
bank.customers.add(customer)
try
bank.customers.add(customer, through_defaults={'city_id':SOME_CITY_OBJ_ID_YOU_WANT})
this works on .set()
function as well
Upvotes: 2