I have two models
class A(models.Model):
title = models.CharField()
class B(models.Model):
heading = models.ForeignKey(A)
content = models.CharField()
When I tried to do the below I'm getting IntegrityError: b.heading may not be null
b = B()
a = A()
b.heading = a
b.heading.title = 'title'
b.content = 'content'
b.heading.save()
b.save()
Why is this happening? can't we work with objects this way?
This actually has to do with the order you do things.
When first assigning a to b.heading, the A object is not yet saved and doesn't have a primary key. Thus, on assignment, b.heading_id will remain None. b.heading doesn't know anything about b, so when saved, b.heading_id will remain None. When saving b, it expects b.heading_id to have a valid non-null value, which it obviously hasn't. Therefore, an IntegrityError is raised.
What you should do, is do the assignment after saving a:
b = B()
a = A()
a.title = 'title'
a.save()
b.heading = a
b.content = 'content'
b.save()
Related
Assume, we have model
class BaseModel(models.Model):
is_a = models.BooleanField()
and two models related to this one:
class A(models.Model):
value_1 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='a')
class B(models.Model):
value_1 = models.IntegerField()
value_2 = models.IntegerField()
base = models.ForeignKey(BaseModel, related_name='b')
What I need is to refer to A or B depending on is_a property.
For example,
base = BaseModel.objects.get(id=1)
if base.is_a:
obj = A.objects.create(value_1=1, base=base)
else:
obj = B.objects.create(value_1=1, value_2=2, base=base)
return obj
or
if base.is_a:
queryset = base.a.all()
else:
queryset = base.b.all()
return queryset
i.e., every time I have to check the is_a property.
Is there more graceful way?
There are two only related models, A and B, no other ones will appear in the nearest future.
Part of the problem can be solved with django-polymorphic, e.g.:
class A(PolymorphicModel):
...
class B(A):
...
This allows to retrieve all A's and B's with one request like base.b.all(), but the problem here is that every B creates instance of A, which is unwanted.
I've considered GenericForeignKey as well. As far as I understood it has a number of limitations like "1) You can't use GenericForeignKey in query filters ; 2) a GenericForeignKey won't appear in a ModelForm" (from GenericForeignKey or ForeignKey).
One idea is to add choices to the BaseModel to have a string representation of your boolean value. If you set the strings equal to the A and B model names, you can use the model.get_foo_display() method to return the name of the model. Then use the Python getattr() method to access attributes as variables.
class BaseModel(models.Model):
base_model_choices = (
(True, 'A'),
(False, 'B'),
)
is_a = models.BooleanField(choices=base_model_choices)
For example,
base = BaseModel.objects.get(id=1)
queryset = base.getattr(models, get_is_a_display()).all()
obj = getattr(models, get_is_a_display()).objects.create(base=base)
I have these four models all related by ManyToManyFields:
A <--> B <--> C <--> D
Given an A object I need to set a value in its related field in D.
class A(models.Model):
name = models.CharField()
# Get all foo, from differents B, C and D
#property
def get_all_foo(self):
for b in B.objects.filter(...):
for c in C.objects.filter(...):
foo = ';'.join([d.foo for d in D.objects.filter(...)])
return foo
def set_foo(self, value):
# the code I need
class B(models.Model):
a = ManyToManyField(A)
class C(models.Model):
b = ManyToManyField(B)
class D(models.Model):
c = ManyToManyField(C)
foo = models.TextField()
What I need is to set the value of foo in D from a function in A (the same way I get all the values from it). How could I do it?
Firstly, your get_all_foo method is incredibly inefficient, potentially causing thousands of separate database calls. Replace it with:
return ';'.join(D.objects.filter(c__b__a__=self).values_list('foo'))
You can use the same principle to update the D objects from A:
D.objects.filter(c__b__a__=self).update(foo=value)
I have models below
class ModelA(models.Model):
a = models.CharField()
b = models.ForeignKey(ModelB)
class ModelB(models.Model):
c = models.CharField()
Is is possible to create an instance of ModelB without saving it, assign to ModelA?
something like below
obj_b = ModelB(c='1')
obj_b.save(commit=False)
obj_a = ModelA(a='2', b=obj_b)
obj_a.save(commit=False)
# change obj_b attributes in between
# change obj_a attributes in between
obj_b.save()
obj_a.save()
The short answer is no it's not, because the ORM doesn't seem to pick up that obj_b has been saved, however that shouldn't be a problem, because all you need to do is assign obj_a.b = obj_b after saving obj_b. This should work:
obj_b = ModelB(c='1')
obj_a = ModelA(a='2')
# change obj_b attributes in between
# change obj_a attributes in between
obj_b.save()
obj_a.b = obj_b
obj_a.save()
(Note, obj_b.save(commit=False) is invalid and will raise an error.)
My models.py looks like that :
class B(models.Model):
...
class C(models.Model):
...
class D(models.Model):
...
class A(models.Model):
b = models.ForeignKey(B, blank=True, null=True)
c = models.ForeignKey(C, blank=True,null=True)
d = models.ForeignKey(D, blank=True,null=True)
In views.py, I have to initialize an empty A, and fill it later. I can use
i = A().save()
But when I try something like :
i.b.add(objectb)
This exception occurs :
'NoneType' object has no attribute 'b'
I tried to cancel the null=True :
class A(models.Model):
b = models.ForeignKey(B, blank=True)
c = models.ForeignKey(C, blank=True)
d = models.ForeignKey(D, blank=True)
But then another exception occured :
A.b_id may not be NULL
I can't figure out how to initialize an empty but not "None" A.
I need your help,
Thanks you
I think you are confusing ManyToManyField and ForeignKey
To initialize a Foreign key, you would just do:
i = A().save()
i.b = objectb
i.save()
where objectb is an instance of class B
The reason i.b.add(objectb) throws an exception is,
i.b is accessing the attribute b of the model instance i, which is None, and when the ORM tries to call the add on b, it is indeed None. Hence the error.
Try this:
a = A()
b = B()
b.save()
a.b = b
a.save()
Is it possible to instantiate a subclassed model from its parent?
class Object1(models.Model):
field1a = models.CharField()
field1b = models.CharField()
feild1c = models.ForeignKey(Object4)
class Object2(Object1):
field3 = models.CharField()
class Object3(Object1):
field3 = models.CharField()
class Object4(models.Model):
field4 = models.CharField()
What I want to do is create the base class first and then based on some rule instantiate one of the subclasses but using the already created base class.
Something like:
obj4 = Object4(field4='d')
obj1 = Object1(field1a='a', field1b='b', field1c=obj4)
if somerule:
obj2 = Object2(object1_ptr=obj1, field2='2')
else:
obj3 = Object3(object1_ptr=obj1, field3='3')
I don't want to repeat the Object1 fields in the if/else clauses. Is it possible to accomplish this? When I try this I get a Foreign key error;
Cannot add or update a child row: A foreign key constraint fails
I recommend doing something like this:
attr = dict(field1a='a', field1b='b', field1c=obj4)
obj1 = Object1(**attr)
if somerule:
attr["field2"] = 2
obj2 = Object2(**attr)
else:
attr["field3"]='3'
obj3 = Object3(**attr)
Be aware that the dictionary attr changes in place.
What you're doing is almost correct, but if you want to copy it you will have to remove the primary key.
So... this should fix it: del obj2.id
Do note however that if some other model references your model with a foreign key that it references obj1, not obj2. And obj1 will, ofcourse, still exist.