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()
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 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)
Let's start from the botton, this is what I want to achieve
class ClassA(model.Models):
pass
class Class1(model.Models):
fieldX = models.CharField()
class Class2(Class1):
fieldY = models.BooleanField()
# all models are connected, and I prefere to have this situation
# I want to call related ClassA from both Class1 and Class2 using the
# same property
c1 = Class1()
c1.class_a
c2 = Class2()
c2.class_a
# and, at the same time, call both Class1 and Class2 from ClassA
a = ClassA()
a.class_1
a.class_2
To solve this I am duplicating code (and I don't like it):
class Class1(model.Models):
fieldX = models.CharField()
class Class2(model.Models):
fieldY = models.BooleanField()
fieldX = models.CharField()
class ClassA(models.Models):
class_1 = models.OneToOneField(Class1, reverse_name='class_a')
class_2 = models.OneToOneField(Class2, reverse_name='class_a')
The fields are much more than this dummy examples and modify them is quite annoying. How can I do that? I have lots of throubles with "reverse_name" field errors to achieve this, is there a workaroud?
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)
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()