How to use reverse foreign key relationship twice in one query - python

I've the models defined as follows:
class A(models.Model):
name_a = models.CharField()
class B(models.Model):
a = models.ForeignKey(A, related_name='aa')
name_b = models.CharField()
class C(models.Model):
b = models.ForeignKey(B, related_name='bb')
name_c = models.CharField()
I'm making a RESTful webservice for model A and I've a queryset defined as
queryset = A.objects.prefetch_related('aa').all()
But I want data from model C as well. But I'm not sure how can I do that?

You can traverse relationships with the double underscore:
queryset = A.objects.prefetch_related('aa','aa__bb').all()
It becomes clearer without using related names:
class A(models.Model):
name = models.CharField()
class B(models.Model):
a = models.ForeignKey(A)
name = models.CharField()
class C(models.Model):
b = models.ForeignKey(B)
name = models.CharField()
A.prefetch_related('b_set','b_set__c_set').all()
Example from the documentation:
>>> Restaurant.objects.prefetch_related('pizzas__toppings')
Where Pizza has a Many-to-Many relationship with Toppings.

Related

Dehydrate the same field django

I have a model Student:
class Student(models.Model):
id = models.AutoField(primary_key=True)
name = models.CharField(max_length=30, verbose_name='Name')
lastname = models.CharField(max_length=100, verbose_name='Lastname')
history = HistoricalRecords()
Also I have a model:
class Class(models.Model):
id = models.AutoField(primary_key=True)
student = models.models.ForeignKey(Student,on_delete = models.CASCADE, related_name='+')
my admin.py
class ClassResource(resources.ModelResource):
class Meta:
model = Class
fields = ('student',)
def dehydrate_student(self, Class):
student= getattr(Class.student, "name")
return '%s' % (student)
class ClassExportAdmin(ImportExportModelAdmin, ClassAdmin):
resource_class = ClassResource
admin.site.register(Class, ClassExportAdmin)
Now I am executing only name, is that possible to dehydrate the same field student one more time. I need past into my 2 column the surname of the student.
To export both Student.name and Student.lastname you can directly reference a Foreign Key relation in the fields parameter (docs):
class ClassResource(resources.ModelResource):
class Meta:
model = Class
fields = ('student__name', 'student__lastname')
This means that the column names will appear in your export as:
student__name
student__lastname
If you want the name to be different, you can directly declare a field:
name = Field(
column_name='name',
attribute='name',
widget=ForeignKeyWidget(Student, 'name'))
This will then appear in the export under name.

Django: How to create a dynamic related_name for an inherited parent model?

I have 4 models:
class User(models.Model):
name = models.CharField(max_length=255)
class A(models.Model):
user= models.ForeignKey("User", related_name="u_a", on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class B(A):
user= models.ForeignKey("User", related_name="u_b", on_delete=models.CASCADE)
#isn't the code repeated???
b_field = CharField(max_length=255)
class C(A):
user= models.ForeignKey("User", related_name="u_c", on_delete=models.CASCADE)
#isn't the code repeated???
c_field = CharField(max_length=255)
Here, A has a ForeignKey relationsip with User and a reverse relationship as u_a. But B and C are children of A.
So It appears to me as if Do not repeat your code is violated. How to overcome this?
To work around this problem, In your model class A(models.Model) The part of the value should contain '%(app_label)s' and/or '%(class)s'. see the doc
'%(class)s' is replaced by the lower-cased name of the child class that the field is used in.
'%(app_label)s' is replaced by the lower-cased name of the app the child class is contained within. Each installed application name must be unique and the model class names within each app must also be unique, therefore the resulting name will end up being different.
class A(models.Model):
user= models.ForeignKey("User", related_name="%(class)s_set",
on_delete=models.CASCADE)
#user= models.ForeignKey("User", related_name="%(app_label)s_%(class)s_set",
#on_delete=models.CASCADE)
title = models.CharField(max_length=255)
class B(A):
b_field = CharField(max_length=255)
class C(A):
c_field = CharField(max_length=255)

Django complex relationship

I've the following models:
class A(models.Model):
title = models.CharField(max_length=30)
class B(models.Model):
title = models.CharField(max_length=255)
a = models.ManyToManyField(A)
I now want a model C which has a field that has multiple unique combinations of A and B like in ManyToMany field. I found that for mapping ForeignKey to two models GenericForeignKey can be used. I've tried GenericForeignKey like below:
class C(models.Model):
title = models.CharField()
property1 = models.CharField()
a = models.ManyToManyField(A)
b = models.ManyToManyField(B)
a_b = GenericForeignKey(store, category)
But, this doesn't seem to work. Can someone point me in the right direction? I'm new to Django as well as SQL.
Edit: Clarified the requirement of combinations.
Edit2: Current working model:
I've created below models as a working solution:
class A(models.Model):
title = models.CharField(max_length=30)
class B(models.Model):
title = models.CharField(max_length=255)
a = models.ManyToManyField(A)
class C(models.Model):
title = models.CharField()
property1 = models.CharField()
class A_B_C(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
c = models.ForeignKey(C)
class Meta:
unique_together = ('a', 'b', 'c')
It is this model that I was avoiding to create explicitly and maintain within relations. I would like to know if this solution would be efficient or is there another Django way to do things?
When using ManyToManyField Django creates intermediate table for you implicitly. In your example you'll get additional table with columns (id, a_id, b_id). But you can define it explicitly and set a through option on a field a on B model, and then define unique_together for this intermediate model:
class A(models.Model):
title = models.CharField(max_length=30)
class B(models.Model):
title = models.CharField(max_length=255)
a = models.ManyToManyField(A, through='C')
class C(models.Model):
a = models.ForeignKey(A, on_delete=models.CASCADE)
b = models.ForeignKey(B, on_delete=models.CASCADE)
class Meta:
unique_together = ("a", "b")
Note: If I remember correctly, you can't set unique_together on ManyToManyFiled directly.
This isn't a job for GenericForeignKey. Instead you need to define the unique_together Meta attribute:
class C(models.Model):
...
class Meta
unique_together = ('a', 'b')
For future reference:
I've created below models as a working solution:
class A(models.Model):
title = models.CharField(max_length=30)
class B(models.Model):
title = models.CharField(max_length=255)
a = models.ManyToManyField(A)
class C(models.Model):
title = models.CharField()
property1 = models.CharField()
class A_B_C(models.Model):
a = models.ForeignKey(A)
b = models.ForeignKey(B)
c = models.ForeignKey(C)
class Meta:
unique_together = ('a', 'b', 'c')

Reverse Inlines in Django Admin

I have 2 models as follows. Now I need to inline Model A on Model B's page.
models.py
class A(models.Model):
name = models.CharField(max_length=50)
class B(models.Model):
name = models.CharField(max_length=50)
a = models.ForeignKey(A)
admin.py
class A_Inline(admin.TabularInline):
model = A
class B_Admin(admin.ModelAdmin):
inlines = [A_Inline]
is that possible?? If yes please let me know..
No, as A needs to have a ForeignKey to B to be used as an Inline. Otherwise how would the relationship be recorded once you save the inline A?
You cannot do it as told by timmy O'Mahony. But you can make B inline in A if you want. Or maybe you can manipulate how django display it in
def unicode(self):
models.py
class A(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class B(models.Model):
name = models.CharField(max_length=50)
a = models.ForeignKey(A)
admin.py
class B_Inline(admin.TabularInline):
model = B
class A_Admin(admin.ModelAdmin):
inlines = [
B_Inline,
]
admin.site.register(A, A_Admin)
admin.site.register(B)
Or maybe you want to use many-to-many relationship?
models.py
class C(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class D(models.Model):
name = models.CharField(max_length=50)
cs = models.ManyToManyField(C)
admin.py
class C_Inline(admin.TabularInline):
model = D.cs.through
class D_Admin(admin.ModelAdmin):
exclude = ("cs",)
inlines = [
C_Inline,
]
admin.site.register(C)
admin.site.register(D, D_Admin)
Of course you can do this. Every relationship be it 1-1, m2m or FK will have a reverse accessor. (more on the FK reverse access here)
class A_Inline(admin.TabularInline):
model = A.b_set.through
class B_Admin(admin.ModelAdmin):
inlines = [A_Inline]

How to select all objects that are foreign keyed from another model in django?

Sorry for title, I do not know how else to express myself.
For example, I have this three models:
class Person(models.Model):
name = models.CharField()
class Teacher(models.Model):
person = models.ForeignKey(Person)
subject = models.CharField()
class Student(models.Model):
person = models.ForeignKey(Person)
grade = models.CharField()
How can I select all Person models that are Teachers?
Person.objects.filter(teacher__isnull=False)
# return Person who has a teacher pointing to it

Categories

Resources