What I want to do?
I have an app have three models:Series,Section and Episode,every one have a one-many query(by ForeignKey). just like this
series-> many Section,section-> many Episode
Now I will show a section with episodes information in series page, but it make more query.
now code
views.py
series = Series.objects.get(id=series_id)
section = Section.objects.filter(series=series)
list.html
{% for item in sections %}
...
{% for episode in item.episode_set.all %}
...
{% endfor %}
...
{%endfor%}
models.py
class Section(models.Model):
series = models.ForeignKey(Series)
....
class Episode(models.Model):
section = models.ForeignKey(Section)
What I want to get ?
an example code tell me how to query in views.py and use just little query.
you can guess, in my code, if here are many section and many episode, it will have many query.
Some idea.
I use Laravel Before, In Laravel , it has a hasMany Function, I can use it to get other models items(Define by belongsTo).
Is Django has same function?
If in template you need only episodes, you can query just them with single query
Episode.objects.filter(section__series=series_id)
But if you need all data then you can use prefetch_related Django docs.
The simplest example looks enough for your code
series = Series.objects.filter(id=series_id).prefetch_related('section_set', 'section_set__episode_set').get()
After this calls to series.section_set.all() and to section.episode_set.all() will not produce extra query.
Related
I have Django app, which have following statements:
response = render_to_response('template.html', {'s':s, 'r':r}, context_instance=RequestContext(request))
The typical statement at template.html is:
<tbody>{%for m in r.m_set.all %}<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot.id}}</td>
<td>{{m.MaterialSublot.id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure.id}}</td>
<td>{{m.Status.id}}</td></tr> {%endfor%}</tbody>
There are about 10 thousands records. the page response time take about 3 minutes(ThinkServer, Linux, Apache, mod_wsgi, Python3.4, Django 1.9.4, MySQL), is this normal?
Thanks!
Every call to {{ m.XXX.id }} leads to separate SQL-query for the XXX instance. To avoid this you should use the select_related() method of the queryset.
Add the following method to the class of the r variable:
def get_all_m(self):
return self.m_set.all().select_related()
and then use it in the template:
{% for m in r.get_all_m %}
To only access the id of the related models, add the _id suffix to the field name. This data is already available because that's the value of the foreign key, so you don't need extra queries or joins. However, you are accessing the Name property of the StorageLocation field, so you need a join to prevent doing a query in every iteration.
Your call for the data becomes:
r.m_set.select_related('StorageLocation').all()
Your template becomes:
<tbody>
{%for m in r.m_set.all %}
<tr>
<td>{{m.id}}</td>
<td>{{m.Name}}</td>
<td>{{m.MaterialLot_id}}</td>
<td>{{m.MaterialSublot_id}}</td>
<td>{{m.Description|truncatechars:20}}</td>
<td>{{m.StorageLocation.id}} - {{m.StorageLocation.Name}}</td>
<td>{{m.Quantity|floatformat:"-3"}}</td>
<td>{{m.QuantityUnitOfMeasure_id}}</td>
<td>{{m.Status_id}}</td>
</tr>
{%endfor%}
</tbody>
Just remember that if you need access to a field other than id on a related model, add the field name to the select_related() call.
I have been dabbling with Django CBV lately and have a question. Maybe you have better ideas than I.
Assume I have a airline booking CRM application and I intend to perform a display of a customer for various things. Assume I have a list of Models, for a Customer like Booking, Rating, Customer_Service_Calls, Favourited_Route.
Now, given a DetailView implemented by Django's CBV, I have something like this
class CustomerThreeSixtyView(DetailView):
model = 'Customer'
def get_context_data(self, **kwargs):
context = super(CustomerThreeSixtyView, self).get_context_data(**kwargs)
context['bookings'] = Booking.objects.all.filter(customer_id=request.kwargs['pk']
context['ratings'] = Ratings.objects.all.filter(customer_id=request.kwargs['pk']
context['calls'] = Customer_Service_Calls.objects.all.filter(customer_id=request.kwargs['pk'], status'Open')
context['fav_routes'] = Favourited_Route.objects.all.filter(customer_id=request.kwargs['pk'], status'Open')
return context
Something like this. My question is that, are there better ways to do this? This is the most straightforward way but I'm asking for suggestions because there seem to be bound for something.
What you have done already looks good enough. You are getting what you required in the context and then using it in the template to show the information.
Alternatively, you could directly access bookings for a particular customer in the template without specifying it in the context:
{% for booking in object.booking_set.all %} # object is the customer here
# do what you want to do with the booking here
{% endfor %}
It is even better if you use related_name while linking the customer to Booking:
class Booking(models.Model):
customer = models.ForeignKey(Customer, related_name='bookings')
# other fields
Now, you can directly use the defined related_name to access the bookings for a particular customer:
{% for booking in object.bookings.all %}
# do what you want to do with the booking here
{% endfor %}
And, you can use the same approach for other classes such as Rating, Customer_Service_Calls, Favourited_Route etc.
There is a common case I encounter, where I can't find a way to split apps.
The case is when a info of two models is related and needs to be in the same template
An example speaks 1000 words: (2 models - pages + comments).
# models.py
class Page(models.Model):
title = models.CharField()
content = models.TextField()
class Comment(models.Model):
page = models.ForeignKey('Page')
content = models.TextField()
# url.py
...
url(r'^page/(?P<page_pk>\d+)/$', views.ViewPage, name='page-view-no-comments'),
url(r'^comment/(?P<comment_pk>\d+)/$', views.ViewComment, name='comment-view'),
url(r'^page-with-comments/(?P<page_pk>\d+)/$', views.ViewPageWithComments, name='page-view-with-comments'),
...
# views.py
def ViewPage(request, page_pk):
page = get_object_or_404(Page, pk=page_pk)
return render(request, 'view_page.html', {'page':page,})
def ViewComment(request, comment_pk):
comment = get_object_or_404(Comment, pk=comment_pk)
return render(request, 'view_comment.html', {'comment':comment})
def ViewPageWithComments(request, page_pk):
page = get_object_or_404(Page, pk=page_pk)
page_comments = Comment.objects.filter(page=page)
return render(request, 'view_page.html', {'page':page,'page_comments':page_comments'})
In this situation, splitting to Page app and Comment app is problematic, because they share a view (ViewPageWithComments) and url.
My options are:
1) Create an Ajax call to comments, which has crawling problems although Google might have fixed it lately.
2) Create a method of page that calls a method in the comments app that returns html with the comments content. If the method needs more arguments I also need to write a custom filter tag.
3) Decide not to split...
Am I missing something and there's another option? When would you prefer (1) vs (2) ?
Note - I created a very simple example to keep the problem general.
You don't need to split anything, you have the pages, and comments have a foreign key to that so you can just iterate over the pages comments
{% for page in pages %}
{% for comment in page.comment_set.all %}
{% endfor}
{% endfor %}
If you want to be able to use the same template for a version of this page without comments you can just wrap the comment for loop in an {% if show_comments %} statement
I want to create a custom model with methods that connect to an existing DB and return data based on custom queries injected into Django. I am obviously having some problems with this, and am wondering if this is possible with my current approach. I feel like this is either a trivial question or I am misunderstanding something fundamental here. Here is my code so far:
from django.db import models
import datetime
from django.utils import timezone
class data_model(models.Field):
description = "return and create data objects for visulaizations"
def __init__(self, days, action):
self.days = days
self.action = action
if(self.action == ""):
self.action = "inspections"
getVioPoints(self.action)
def getVioPoints(self):
#get points query here and get data from db
return self
Within getVioPoints, I want to create a query string - like what you would see in a PHP $query = "select tuples where what I want = something" type of situation. Then return the data in a way that my template views can access it like so:
<ul>
{% for choice in data_returned %}
<li> {% choice.title %} </li>
{% endfor %}
</ul>
Any ideas? Any assistance would be greatly appreciated.
Thanks.
You can make custom query and put result into json.
You can read about custom queries here:https://docs.djangoproject.com/en/dev/topics/db/sql/#executing-custom-sql-directly,
but this isn`t django-way - you just ignore orm layer and have to write all sql queries by yourself, I recommend you to create model, connect it to db and make queries through orm.
Alright, I have a Django view, like this:
#render_to('home/main.html')
def login(request):
# also tried Client.objects.select_related().all()
clients = Client.objects.all()
return {'clients':clients}
And I have a template, main.html, like this:
<ul>
{% for client in clients %}
<li>{{ client.full_name }}</li>
<ul>
{% for pet in client.pets.all %}
<li>{{ pet.full_name }}</li>
{% endfor %}
</ul>
{% endfor %}
</ul>
I also print out all the queries in sql_queries at the bottom of my base template. When I run this view, the following queries are made:
SELECT `home_client`.`id`, ... FROM `home_client`;
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 1;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 1;
SELECT `home_pet`.`id`, ... FROM `home_pet` WHERE `home_pet`.`client_id` = 2;
SELECT `home_client`.`id`, ... FROM `home_client` WHERE `home_client`.`id` = 2;
My question is, why are all these queries being made? Shouldn't it just be 1 query to retrieve all the clients and a query per client to retrieve all the pets from each client? I have 2 clients in the home_client table, so it should be 3 queries total. Most troubling of all is that queries 3 and 4 are 100% identical. I don't want to "prematurely optimize" or anything but I do want to make sure Django isn't being wildly inefficient. Any help on this would be appreciated. Thanks.
Django uses a cache. The RDBMS uses a cache. Don't prematurely optimize the queries.
You can play with bulk queries in your view function instead of one-at-a-time queries in your template.
#render_to('home/main.html')
def login(request):
# Query all clients
clients = Client.objects.all()
# Assemble an in-memory table of pets
pets = collections.defaultdict(list)
for p in Pet.objects.all():
pets[pet.client].append(p)
# Create clients and pets tuples
clientsPetTuples = [ (c,pets[c]) for c in clients ]
return {'clientPets': clientsPetTuples}
However, you don't seem to have any evidence that your template is the slowest part of your application.
Further, this trades off giant memory use against SQL use. Until you have measurements that prove that your template queries are actually slow, you shouldn't be over thinking the SQL.
Don't worry about the SQL until you have evidence.
try using Client.objects.all().select_related()
This will automagically also cache related models in a single database query.
Does Client 1 have 2 Pets and Client 2 have 1 Pet?
If so, that would indicate to me that Pet.full_name or something else you're doing in the Pet display loop is trying to access its related Client's details. Django's ORM doesn't use an identity map, so accessing the Client foreign key from any of your Pet objects would require hitting the database again to retrieve that Client.
P.S. select_related won't have any effect on the data you're using in this scenario as it only follows foreign-key relationships, but the pet-to-client relationship is many-to-one.
Update: if you want to avoid having to change the logic in Pet.full_name or having to perform said logic in the template instead for this case, you could alter the way you get a handle on each Client's Pets in order to prefill the ForeignKey cache with for each Pet with its Client:
class Client(models.Model):
# ...
def get_pets(self):
for pet in self.pets.all():
setattr(pet, '_client_cache', self)
yield pet
...where the 'client' part of '_client_cache' is whatever attribute name is used in the Pet class for the ForeignKey to the Pet's Client. This takes advantage of the way Django implements access to ForeignKey-related objects using its SingleRelatedObjectDescriptor class, which looks for this cache attribute before querying the database.
Resulting template usage:
{% for pet in client.get_pets %}
...
{% endfor %}