I'm having trouble doing a query spanning between a lot of models.
This is the query I do to display all animals and their corresponding vaccines with encounter.status 'in-progress' and the date immunization date is in the futur.
def current_with_futur_vaccines(self):
return (
Encounter.objects.filter(
status="in-progress").filter(
subject__immunizations__recorded__gte=datetime.now(),
)
.select_related("subject")
.prefetch_related("subject__immunizations", "location")
)
The things is when I want to list the immunizations from the query I get all the immunizations for this animal and not only the immunizations that have to take place in the futur.
{% for immunization in object.subject.immunizations.all %}
{{ immunization }}
{% endfor %}
This is the model
class Animal(models.Model):
name = models.CharField(max_length=250)
class Encounter(models.Model):
subject = models.ForeignKey(Animal, on_delete=models.PROTECT)
status = models.CharField(max_length=11)
class Vaccine(models.Model):
name = models.CharField(max_length=250)
class Immunization(models.Model):
subject = models.ForeignKey(
Animal, on_delete=models.PROTECT, related_name="immunizations"
)
recorded = models.DateTimeField(default=timezone.now)
vaccine = models.ForeignKey(Vaccine, on_delete=models.PROTECT)
EDIT
When I call all() I don't expect to have all the results related to the FK. What I want is the results filtered by the query. In my example I have all the encounters filtered with recorded date in the futur but when I can the related Imunization entries with .all() I don't get the filtered Immunization results filtered by recorded but all of them.
EDIT 2:
I think I have figured it out by using a filtered prefetch. Something like this.
def current_with_futur_vaccines(self):
immunizations_prefetch = models.Prefetch("subject__immunizations", Immunizations.objects.filter(recorded__gte=datetime.now())
return (
Encounter.objects.filter(
status="in-progress").filter(
subject__immunizations__recorded__gte=datetime.now(),
)
.select_related("subject")
.prefetch_related(immunizations_prefetch, "location")
)
You don't have a Foreign Key/M2M reference to Immunizations in you Animal Model.
Related
This is a simplified version of the models:
class Toy(models.Model):
#generic fields
class Order(models.Model):
customer = models.ForeignKey(Customer)
class OrderItem(models.Model):
order = models.ForeignKey(Order)
toy = models.ForeignKey(Toy)
points = models.PositiveSmallIntegerField(default=3)
A customer can make multiple orders, to it increases the number of points per toy.
This subquery, only returns the first row of OrderItem:
class Customer(models.Model):
def get_toys_with_points():
order_items = OrderItem(toy=models.OuterRef('pk'), order__customer=self)
toys = Toy.objects.annotate(
points_sum = models.Sum(Subquery(order_items.values('points')))
)
return toys
So, when I pull that into my template:
{% for toy in customer.get_toys_with_points %}
{{ toy.points_sum }}
{% endfor %}
I am always getting the value of the first row (even if there are more purchases that would sum up to 25 for example).
You don't need a subquery here.
toys = Toy.objects.filter(orderitem__order__customer=self).annotate(
points_sum=models.Sum('orderitem__points')
)
i want to make a ticket system. I have the main model (TicketSystem) and a model with the messages from the users (TicketSystem_Messages).
In the model "TicketSystem_Messages" is the ForeignKey to the model "TicketSystem".
Here is my code:
class TicketSystem(models.Model):
subject = models.CharField(_('Subject'),max_length=30, blank=False, default="N/A")
message = models.TextField(_('Message'), null=False, blank=False)
created_date = models.DateTimeField(_('Date'), default=datetime.utcnow().replace(tzinfo=utc))
fertig_date = models.DateTimeField(_('Date'), default=datetime.utcnow().replace(tzinfo=utc))
class TicketSystem_Messages(models.Model):
user_name = models.ForeignKey(User)
status = models.ForeignKey(TicketSystem_Status)
ticketid = models.ForeignKey(TicketSystem)
message = models.TextField(_('Message'), null=False, blank=False)
created_date = models.DateTimeField(_('Sent'), default=datetime.utcnow().replace(tzinfo=utc))
At the moment i get the Tickets without the Messages:
sql_TicketSystem = TicketSystem.objects.filter(id=kwargs['pk'])
I want to make a LEFT JOIN like this
SELECT * FROM TicketSystem LEFT JOIN TicketSystem_Messages ON Ticketsystem.id = TicketSystem_Messages.ticketid
I heard something about "select_related" and "prefetch_related" and tried it but it does not work.
Rather than using raw SQL joins, in Django you can traverse model relationships in either direction, regardless of how they are represented in the database.
To find all the messages for a given TicketSystem instance:
my_ticket = TicketSystem.objects.get(id=0) # or something
my_ticket_messages = my_ticket.ticketsystem_messages_set.all() # or do other filters here
To find all the messages using a queryset:
TicketSystem_Messages.objects.filter(ticketId=my_ticket)
To find all tickets with more than one message:
from django.db import Count
TicketSystem.objects.annotate(message_count=Count('ticketsystem_messagess')).filter(message_count__gt=1)
If i want to list all tickets with the last status from the model "TicketSystem_Messages", it doesnt work. So with "TicketSystem.objects.get" it works without any problems.
Example:
sql_TicketSystem = TicketSystem.objects.all()
sql_TicketSystem_Messages = sql_TicketSystem.ticketsystem_messages_set.all()
UPDATE:
{% for data in sql_TicketSystem %}
{{ data.subject }}
{% for msg in data.ticketsystem_messages_set.all %}
{{msg.status}}
{% empty %}
Unknown
{% endfor %}
{% endfor %}
This works :-)
We are on Django 1.5 (for now, until the boss can be convinced otherwise...)
The app has multiple objects that reference each other as foreign keys:
class Result(models.Model):
quiz = models.ForeignKey(Quiz)
user = models.ForeignKey(User)
class Quiz(models.Model):
quiz_type = models.CharField(max_length=1, choices=TYPE_CHOICES, default=TYPE_POST)
name = models.CharField(max_length=200)
lesson = models.ForeignKey(Lesson, blank=True, null=True, db_index=True)
class Lesson(models.Model):
title = models.CharField(max_length=200)
unit = models.ForeignKey(Unit)
class LessonStatus(models.Model):
user = models.ForeignKey(User)
lesson = models.ForeignKey(Lesson)
attempts_remaining = models.IntegerField(default=1)
passed = models.PositiveIntegerField(default=0)
For reporting, I need to get all Results for a specified user (or users), apply some filters based on user input, and then display a table with data that includes fields from User, Quiz, Lesson, and Unit. (It also checks LessonStatus for some display options.)
Right now, most of the foreign key queries are being done in a loop in the template, which is.. bad. I tried using select_related - my query log shows that it is successfully doing the table joins, but doesn't actually assign the fields to anything, so it seems that when I reference a related field, it's still causing a new query.
I have found similar questions, where the answers suggested using annotate, but that's not working for me.
I have tried:
r = Result.objects.filter(user__in=students).filter(~Q(quiz__quiz_type='3'))
r.annotate(qt=F('quiz__quiz_type'))
but that gets me : 'F' object has no attribute 'lookup'. This is the same whether or not I do a select_related('quiz'). What am I doing wrong?
Adding code from view:
r = Result.objects.filter(user__in=students).filter(~Q(quiz__quiz_type='3'))
r.select_related('quiz').select_related('user').select_related('lesson').select_related('unit')
if test_type != 'all':
r = r.filter(Q(quiz__quiz_type=test_type))
quiz__quiz_type='3')
if grade != 'any':
r = r.filter(Q(quiz__lesson__unit__grades=grade))
#more filters
for test in r:
if test.quiz.quiz_type == "1":
tu = test.user_id
try:
status = LessonStatus.objects.get(user=tu, lesson=test.quiz.lesson.id)
My expectation was that when I do the select_related, it shouldn't need to do additional queries for the related objects. However, I'm seeing queries on Quiz in that forloop, so I'm obviously doing something wrong.
Template:
(in a loop of for result in results)
{{result.user.first_name|title}} {{result.user.last_name|title}}
{{ result.quiz.lesson.title }}
{{ result.quiz.lesson.unit.title }}
{{ result.quiz.get_quiz_type_display }}
Query log is showing queries for each lesson and unit, so again, I'm doing something horribly wrong..
You need to follow the relationships on the select_related call itself; lesson and unit are not fields on Result, so will be ignored. It should be:
Result.objects.filter(...).select_related("quiz__lesson__unit", "user")
I have 3 Models Product,Company Categories.
class Product(Meta):
categories = models.ManyToManyField(Category)
company = models.ForeignKey(Company, related_name='products', on_delete=models.CASCADE)
updated_at = models.DateTimeField(auto_now_add=False, auto_now=True)
I need:
to get all the products of a company
show the product first category
count the number products per company and show
order products by reverse updated_at
I start from:
1. Company.objects.get(pk=company_pk).prefetch_related('products')
will give me an error, because get returns an object:
class CompanyProductListView(ListView):
model = Company
template_name_suffix = '_company_list'
def get_queryset(self):
company_pk = self.kwargs.get('pk')
return Company.objects.get(pk=company_pk).prefetch_related('products')
get without prefetch works.
return Company.objects.filter(pk=company_pk).prefetch_related('products')
there is no error, but in template:
{% for company in company_list %}
{{ company.name}}
{% endfor %}
I loop even is one, but doesn't show me anything.
Besides that I need to attach first category to each product, and count the number of products
I'm thinking on access something like this:
{{company.name}}
{% for product in company.products %}
{{ product.name }}
{{ product.category }}
This query will get a little complicated, but should help you solve your issue.
PS: I haven't tested this but should mostly work. Will take a deeper look once I get some more time.
First we get the company we want:
company = Company.objects.get(pk=company_pk)
Then we fetch all the first categories for all products, it can be done by using this question as a guide:
first_categories = Category.objects.order_by('product__id', '-id').distinct('product__id')
Now we use the first_categories to use to limit the amount of data we prefetch (giving this a different perspective, we will query the Product model instead of the Company model)
product_list = Products.objects.filter(company=company).prefetch_related(
Prefetch('categories', queryset=first_categories)
)
def get_queryset():
company_pk = ...
company = ...
first_categories = ...
product_list = ...
return product_list
I will give my models first and then write description.
class Entry(models.Model):
entry_text = models.TextField()
class Category(models.Model):
user = models.ForeignKey(User)
category_text = models.CharField(max_length=200)
entries = models.ManyToManyField(Entry, through='CategoryEntry')
class CategoryEntry(models.Model):
category = models.ForeignKey(Category)
entry = models.ForeignKey(Entry)
viewed = models.BooleanField(default=False)
So I have Entry model and Category model, and I have created intermediate model CategoryEntry as descriebed here https://docs.djangoproject.com/en/1.7/topics/db/models/#extra-fields-on-many-to-many-relationships because I need one extra field "viewed" (marked as True when user for the first time opens specific Entry link).
So I have created generic.ListView view, where I show all these categories that user has created for himself. What I want, is to show next to every category name, how many entries there are and how many entries he hasn't viewed yet.
Like:
Category Total Not_viewed
AAA 126 5
BBB 17 15
I have managed to show total entries in template by
{% for category in categories %}
{{ category.text }}
{{ category.entries.count }}
{% endfor %}
In my view I have get_queryset like
def get_queryset(self):
categories = Category.objects.filter(user=self.request.user.id)[:]
return categories
As I understand, then the best way would somehow add this extra info about every categories entries viewed count in get_queryset. I have searched around but didn't found anything what works. Have tried some things with select_related, prefetch_related, annotate but don't get whats the right way to do this.
Know that it's not right, but tried something like that and some other things.
categories = Category.objects.filter(user=self.request.user.id).select_related('categoryentry').filter(categoryentry__viewed=False).count()
categories = Category.objects.filter(user=self.request.user.id).annotate(not_viewed_count=Count('categoryentry')).filter(not_viewed_count__viewed=False)
Hope you get my idea what I wan't to achieve.
In your CategoryEntry model, use related_name in the category field like so:
category = models.ForeignKey(Category, related_name="related_entry_categories")
Now you can use this related name when querying the Category model. For example:
from itertools import chain
categories_not_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=False).annotate(num_not_viewed=Count('related_entry_categories'))
categories_viewed = Category.objects.filter(user=self.request.user.id, related_entry_categories__viewed=True).extra(select={'num_not_viewed': 0})
categories = chain(list(categories_not_viewed), list(categories_viewed))
At end I came up with this solution:
categories = Category.objects.filter(user=self.request.user.id).extra(select = {
"num_not_viewed" : """
SELECT COUNT(*)
FROM app_categoryentry
WHERE app_categoryentry.category_id = app_category.id
AND app_categoryentry.viewed = %d """ % 0,
})
Based on the solution from this resource http://timmyomahony.com/blog/filtering-annotations-django/
If anyone have other solution how the get the same result with only Django ORM, I would like to know.