django orm: use annotate case on prefetch result - django

this is a near replica of my models:
class To(models.Model):
pass
class FromA(models.Model):
to = models.ForeignKey(To)
class FromB(models.Model):
to = models.ForeignKey(To)
is there a way to write a query like this?
To.objects.annotate(from=Case(
When(froma__isnull=False, then=Prefetch("froma")),
When(fromb__isnull=False, then=Prefetch("fromb"))
))

Related

join 2 tables that have no foreign key to eact other

class A(Base):
number = models.IntegerField(default=None)
...
class B(Base):
head_number = models.IntegerField(default=None)
...
there is 2 model that have no relation to each other, how its possible to write query below with annotate in django orm?
Select * from A
inner join B on A.number = B.head_number
I had tried with extra() and it works, but I want to have it with annotate().
and it can not change any model .
Is there any way?
You can filter using in field lookup.
A.objects.filter(number__in=B.objects.values_list('head_number', flat=True))
This queryset B.objects.values_list(..) will be evaluated as subselect statement(subquery in where clause)

Double reverse filtering in Django ORM

I am facing one issue while filtering the data .
I am having three models ...
class Product:
size = models.CharField(max_length=200)
class Make(models.Model):
name = models.ForeignKey(Product, related_name='product_set')
class MakeContent(models.Model):
make = models.ForeignKey(Make, related_name='make_set')
published = models.BooleanField()
I can generate a queryset that contains all Makes and each one's related MakeContents where published = True.
Make.objects.filter(make_set__published=True)
I'd like to know if it's possible (without writing SQL directly) for me to generate a queryset that contains all Product and each one's related MakeContents where published = True.
I have tried this
Product.objects.filter(product_set__make_set__published=True)
But it's not working
A subquery can solve the problem.
from django.db.models import Subquery
sub_query = MakeContent.objects.filter(published=True)
Product.objects.filter(
pk__in=Subquery(sub_query.values('make__name__pk'))
)
Use this
Product.objects.filter(make__makecontent__published=True).distinct()
Filtering through ForeignKey works both forward and backwards in Django. You can use the model name in lower case for backward filtering.
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships
Finally .distinct() is required to remove the multiple values produced due to SQL joins

Django: Select data from two tables with foreigin key to third table

I have following models:
class Dictionary(models.Model):
word = models.CharField(unique=True)
class ProcessedText(models.Model):
text_id = models.ForeiginKey('Text')
word_id = models.ForeignKey('Dictionary')
class UserDictionary(models.Model):
word_id = models.ForeignKey('Dictionary')
user_id = models.ForeignKye('User')
I want to make query using django ORM same with next sql
SELECT * FROM ProcessedText, UserDictionary WHERE
ProcessedText.text_id = text_id
AND ProcessedText.word_id = UserDictionary.word_id
AND UserDictionary.user_id = user_id
How to do it in one query without using cycles?
This might help you:
How do I select from multiple tables in one query with Django?
And also you may have to restructure your models to enable select_related concept of django.

How to get object from manytomany?

I have models:
class Z(models.Model):
name = ...
class B(model.Model):
something = model...
other = models.ForeignKey(Z)
class A(models.Model):
date = model.DateTimeField()
objs_b = models.ManyToManyField(B)
def get_obj_b(self,z_id):
self.obj_b = self.objs_b.get(other=z_id)
and query:
qs = A.objects.filter(...)
but if I want get object B related to A I must call get_obj_b:
for item in gs:
item.get_obj_b(my_known_z_id)
It was generate many queries. How to do it simple? I can not change models, and generally I must use filter (not my own manager) function.
If you are using Django 1.4, I would suggest that you use prefetch_related like this:
A.objects.all().prefetch_related('objs_b__other')
This would minimize the number of queries to 2: one for model A, and one for 'objs_b' joined with 'other'
And you can combine it with a filter suggested by pastylegs:
A.objects.filter(objs_b__other__id=z_id).prefetch_related('objs_b__other')
For details see: https://docs.djangoproject.com/en/1.4/ref/models/querysets/#prefetch-related

Quering distinct values throught related model

I have a simple one-to-many (models.ForeignKey) relationship between two of my model classes:
class TeacherAssignment(models.Model):
# ... some fields
year = models.CharField(max_length=4)
class LessonPlan(models.Model):
teacher_assignment = models.ForeignKey(TeacherAssignment)
# ... other fields
I'd like to query my database to get the set of distinct years of TeacherAssignments related to at least one LessonPlan. I'm able to get this set using Django query API if I ignore the relation to LessonPlan:
class TeacherAssignment(models.Model):
# ... model's fields
def get_years(self):
year_values = self.objects.all().values_list('year').distinct().order_by('-year')
return [yv[0] for yv in year_values if len(yv[0]) == 4]
Unfortunately I don't know how to express the condition that the TeacherAssignment must be related to at least one LessonPlan. Any ideas how I'd be able to write the query?
Thanks in advance.
It is recommended to use the ModelManager for table-level ORM as follows:
class TeacherAssignmentManager(models.Manager):
def get_years(self):
year_values = self.filter(lessonplan__isnull=False).\
values_list('year').distinct().\
order_by('-year')
return [yv[0] for yv in year_values if len(yv[0]) == 4]
Add your custom manager model to TeacherAssignment model class
objects = TeacherAssignmentManager()
Now, you can use TeacherAssignment.objects.get_years() to retrieve all the distinct years that are related to at least one LessonPlan.
TeacherAssignment.objects.filter(lessonplan__isnull=False)
Satisfies your query. Get all TeacherAssignments that have a LessonPlan.
Reverse relationships can be queried this way from the parent model.