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?
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)
i need to join a table without a direct relationship.
models.py:
class FooModel():
bar = ForeignKey(Bar)
class BarModel():
pass
class BazModel():
bar = ForeignKey(Bar)
class QuxModel():
foo = ForeignKey(Foo)
tried to reach Foo from Baz but didn't work
viewset.py:
def BazView(viewsets.ModelViewSet):
queryset = model.BazModel.objects.all().prefetch_related('bar').prefetch_related('baz__bar')
serializer_class = serializer.Baz
def get_queryset(self):
return self.queryset
serializer.py
class FooSerializer(serializer.ModelSerializer):
class Meta:
model = FooModel
exlude = []
class BarSerializer(serializer.ModelSerializer):
class Meta:
model = BarModel
exlude = []
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer()
class Meta:
model = BarModel
exlude = []
class QuxSerializer(serializer.ModelSerializer):
class Meta:
model = QuxModel
exlude = []
using prefetch like that i got an error saying that Baz has no foo field.
also would like to get data from QuxModel based on Foo FK...
how could i perform this?
Try this:
model.BazModel.objects.all().select_related('bar').prefetch_related('bar__foomodel_set')
And then in BazSerializer, you can set the source for foo like this:
class BazSerializer(serializer.ModelSerializer):
foo = FooSerializer(source='bar.foomodel_set')
This will tell the foo serializer to get it's data from the FooModel objects using BazModel's bar.
You might have to change foomodel_set to the related name you have set to access FooModel from BarModel.
I would like to find a solution to be able to set "multiple choices" in OneToOneField relation. I want to be allowed to choose between two classes.
Here is the context :
If I have two classes : ClassOne and ClassTwo like this :
class ClassOne(models.Model):
something = models.CharField(max_length=255)
class ClassTwo(models.Model):
something = models.CharField(max_length=255)
I need to create an Agenda (where I will put events) which can be linked either to ClassOne or to ClassTwo.
I need to do something like this :
class Agenda(models.Model):
owner = models.OneToOneField( ClassOne or ClassTwo , on_delete=models.CASCADE)
description = models.CharField(max_length=255)
At the end I would like to be allowed to do that :
agenda_one = Agenda(owner = ClassOne, description = "")
agenda_two = Agenda(owner = ClassTwo, description = "")
Do you have an idea about how to achieve this ?
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()