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()
Related
Say I have the models
class A(models.Model):
class B(models.Model):
class C(models.model):
b = models.ForeignKey(B)
class D(models.Model):
c = models.ForeignKey(C)
a = models.ForeignKey(A)
What would a ORM query look like to select all Bs that are related to C's that are related to a specific A through table D?
As mentioned by the comment on your post you can use the autogenerated related name. But it never hurts to set it yourself.
class C(models.model):
b = models.ForeignKey(B, related_name="c")
class D(models.Model):
c = models.ForeignKey(C, related_name="d")
a = models.ForeignKey(A, related_name="d")
Then:
B.objects.filter(c__d__a=specific_a_obj).distinct()
class C (models.Model):
b = models.ForeignKey(on_delete=models.CASCADE, related_name='c_related')
final query:
c_ids = D.objects.values_list(
'c__id', flat=True).filter(a__id="specific A object id palced here")
query = B.objects.filter(c_related__id__in=c_ids).distinct()
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)
Say I have a model Foo that needs to refer to a value that can be one of several datatypes. To account for that I have several models for each datatype. Is it possible from within the Foo model to create a getValue method that would return the value from the first value method it can find connected with it?
example:
class Foo(models.Model):
def getValue(self):
if self.hasatr('foovalueint'):
return self.foovalueint
elif self.hasatr('foovaluestring'):
return self.foovaluestring
elif self.hasatr('foovaluedatetime'):
return self.foovaluedatetime
class FooValueInt(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.IntegerField()
class FooValueString(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.CharField(max_length=50)
class FooValueDateTime(models.Model):
foo = models.OneToOneField(Foo, on_delete=CASCADE, primary_key=True)
value = models.DateTimeField()
Or is there a better way to solve this multiple datatype issue?
I have two model classes class A and class B. The second class is related to the first using a foreign key. I want to be able to search class A objects by an attribute of class B (inside django admin):
class A(models.Model):
a1 = models.CharField()
a2 = models.CharField()
class B(models.Model):
fk = models.ForeignKey(A, on_delete=models.CASCADE,)
b1 = models.CharField()
class AAdmin(admin.ModelAdmin):
search_fields = ['a1', 'b1']
admin.site.register(A, AAdmin)
Add a related_name to your Foreign Key and use it for the lookup you need:
class A(models.Model):
a1 = models.CharField()
a2 = models.CharField()
class B(models.Model):
fk = models.ForeignKey(A, on_delete=models.CASCADE, related_name='something')
b1 = models.CharField()
class AAdmin(admin.ModelAdmin):
search_fields = ['a1', 'something__b1']
admin.site.register(A, AAdmin)
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()