Django inline formsets and choicefields generate too many db queries - python

I have a model with a number of foreign key fields, e.g. model Product with fields 'type', 'level', 'color', 'intensity' (just a generic example).
I then have a page to edit all products of a given type using a Type form with the products as an inline formset with the option to add additional products inline using extra=10.
The thing i find very strange is that each time when i output one of the foreign key choice fields on the template Django queries the database for the get the options (every time).
For example:
{% for form in formset %}
{{ form.level }}
{{ form.color }}
{{ form.intensity }}
{% endfor %}
With 20 products (and 10 empty extra forms) the above code issues 30 select * from ... from level, color and intensity totaling 90 queries (revealed using the Django Debug Toolbar), where 3 should be sufficient. The options are unlikely to change mid request, but even if they did I definitely wouldn't want some newly added options to appear in the last 5 forms only.
Is there any way to optimize my models/forms/views/templates so that the database doesn't get hammered like this unnecessarily?
-
Disclaimer: I'm relatively new to django and python and can't help thinking there must be a way to address this built in somehow.

field_queryset = Test.objects.all()
for form in formset:
form.fields['test_field'].queryset = field_queryset
Like this.

You can change the queryset used by the formset, then you could use select_related() to generate the FK joins rather than to execute a query at each forloop iteration.

Related

Lots of SQL queries/slow rendering when using inlineformset_factory (and crispy)

Sample project with screenshots: https://github.com/nspo/slowformsets-django-example
Project with Authors, Books and Publishers.
There is a view to update an author where you can change all the books an Author has as well as add new books. The BookForms are created with an inlineformset_factory. Books also have a foreign key reference (publisher) than can be selected in the BookForm.
Rendering:
{% crispy form %}
{% crispy formset formset.form.helper %}
Problem:
For every BookForm, there seems to be a request for all Publishers.
I kind of understand that there is a request for publishers, because they can be selected in an implicit ModelChoiceField. But shouldn't I be able to speed that up with prefetch_related? I have a feeling that the instance of Author passed in lines 12 and 13 of view.py might be "copied" so that any prefetched data is forgotten?
If a direct solution is not possible b/c of ModelChoiceField limitations, it would also work in my case if the Publisher cannot be changed after adding a Book (but only displayed). Not sure how to do that though without lots of changes.
We can't see any code which makes this kind of a guessing game. You mention prefetch_related but it does not show up in the code you are linking to. In general I would do something like this
queryset=Book.objects.prefetch_related('publisher').filter(author=id_author)
BookInlineFormSet = inlineformset_factory(Author, Book, form=BookForm, extra=2, can_delete=False)
bookformset = BookInlineFormSet(instance=author, prefix="main", queryset=queryset)
Also I'm guessing that every book has exactly one publisher. So it would not be prefetch_related but select_related.

Django Multiple Forms Handling

I am new to Django and tried making a view that holds a list of forms and handles them. I am familiar with the FormView provided by django's generic views however it only holds one form in it.
The idea is that the main form I have is split into a few forms, and then the "mini-forms" are held in a list ("forms").
Since I'm splitting the form into parts and saving a list of forms (for reasons like re-usability and such), the template looks something like this (which does the trick):
...
<form ...>
{% for form in forms %}
{{ form.as_p }}
{% endfor %}
<input type="submit"...>
</form>
...
I have been messing around with the view that should handle the forms, validate them and will get the data from them. Since I feel like Im making it more complicated than it should really be, I'd like to ask you to give an example of a simple view for that, that Ill develop further (get
This may be more a comment but I don't have quite enough rep to respond as a comment so I'm putting this in an answer:
If your primary concern is reusability have you considered creating Mixins instead of making a list of forms?
an example of this could be:
from django import forms
class NameFormMixin(object):
your_name = forms.CharField(label='Your name')
class EmailFormMixin(object):
email = forms.EmailField(label='Your Email')
class NameEmailForm(forms.Form,
NameFormMixin,
EmailFormMixin):
pass
you could include your validation functions in the mixins and have general django form functions in the main form class.

Django: 1 view for different users?

In my project, I'd like to split pages between a side menu and a content page.
I'd like to show a sidemenu which is different for user types (there are 5 user types, so there will be 5 sidemenus). But sometimes, the content page to display can be the same for these 5 user types.
I was looking for a "pattern" to handle this, I thought about one, but never seen it in another project. I'd like you to tell me if it's a good or bad practice:
1 URI for all user types (eg. /myapp/display_home)
In views.py, 1 view for all user types. Into the view, I try to know if the user is logged in, and then which type of user we are dealing with. Then, redirect to the HTML, passing in context a variable containing the user type.
In the end, in the template, something like this:
Example of template:
{% if user_type == "anonymous" %}
<!-- display anonymous sidemenu here -->
{% elif user_type == "landlord" %}
<!-- display landlord sidemenu here -->
{% endif %}
<-- Here is the same content for all user types -->
Do you have any suggestion? another pattern to propose?
Check this answer for discussion on user.is_authenticated. You can also check within the view to see who the user is (see here) and modify the context being sent to the template accordingly. Another option would be to modify your view to simply show different types of users different templates.
Lots of ways to accomplish this, not sure if there's a "correct" one.
Your comment made me realize that this is exactly what the Groups feature of the auth package is for. You can assign users to groups, query whether they're a member of the group, and then show content based on that. I apologize that I don't have time to write out sample code, but there's tons of stuff here on SO about it; hopefully that's a good starting point for you.
I don't know if it is good style. But you could use
{% include user_type_sidemenu_template with arg1=... %}
in your template and provide different templates for the sidemenu for each user_type. You can then set the template variable user_type_sidemenu_template to the corresponding template name in your view.

How to generate lots of redundant ajax elements like checkboxes and pulldowns in Django?

I've been getting lots of answers from stackoverflow now that I'm in Django just by searching. Now I hope my question will also create some value for everybody.
In choosing Django, I was hoping there was some similar mechanism to the way you can do partials in ROR. This was going to help me in two ways. One was in generating repeating indexed forms or form elements, and also in rendering only a piece of the page on the round trip.
I've done a little bit of that by using taconite with a simple URL click but now I'm trying to get more advanced. This will focus on the form issue which boils down to how to iterate over a secondary object.
If I have a list of photo instances, each of which has a couple of parameters, let's say a size and a quantity. I want to generate form elements for each photo instance separately. But then I have two lists I want to iterate on at the same time.
Context:
photos : Photo.objects.all()
and
forms = {}
for photo in photos:
forms[photo.id] = PhotoForm()
In other words we've got a list of photo objects and a dict of forms based on the photo.id.
Here's an abstraction of the template:
{% for photo in photos %}
{% include "photoview.html" %}
{% comment %}
So here I want to use the photo.id as an index to get the correct form. So that each photo has its own form. I would want to have a different action and each form field would be unique. Is that possible? How can I iterate on that? Thanks!
Would it be a with? In Python of course it's form = forms[photo.id] but here?
{% endcomment %}
Quantity: {{ oi.quantity }} {{ form.quantity }}
Dimensions: {{ oi.size }} {{ form.size }}
{% endfor %}
What can I do about this simple case. And how can I make it where every control is automatically updating the server instead of using a form at all?
Thanks!
James
I'm not sure I understand your question, but here's what I think you want, expressed in pseudo-code:
for photo in photos:
form = forms[photo.id]
generate_some_html(photo, form)
Probably the best way to achieve this is with inclusion tags: http://docs.djangoproject.com/en/dev/howto/custom-template-tags/#inclusion-tags . Basically, it's a custom tag that relies on another template, similar to an RoR partial. The details can be found in the link I provided, but what you basically need to do is
create a package names "templatetags" in one of your app directories
create a module in that package. Let's take foo_tags.py for example.
Add the following boilerplate to foo_tags.py:
from django import template
register = template.Library()
Write your custom tag, which implements generate_some_html. Make it take the caller's context:
#register.inclusion_tag('relative/path/to/some/template.html', takes_context=True)
def foo_tag(context):
photo = context['photo'] # loop variable in parent template
form = context['forms'][photo.id]
# further computation that you need in order to setup the
# context for relative/path/to/some/template.html can be
# done here...
return locals()
Use the context returned by foo_tag in relative/path/to/some/template.html.
Add {% load foo_tags %} to the parent template. You should probably put this near the top, the same way you put imports near the top of a .py file.
Use your new custom tag in the parent template:
{% for photo in photos %}
{% foo_tag %}
{% endfor %}
Django doesn't comme with backed AJAX like RAIL, but it let you choose any lib you want to use from the start.
You can do what you want by creating custom widgets and using form media.

What does pk__in mean in Django?

Model.objects.filter(pk__in=[list of ids])
and
Model.objects.filter(pk__in=[1,2,3])
How do I show this data in a template?
def xx(request):
return HttpResponse(Model.objects.filter(pk__in=[1,2,3]))
It means, give me all objects of model Model that either have 1,2 or 3 as their primary key.
See Field lookups - in.
You get a list of objects back you can show them in the template like every other list, using the for template tag:
{% for object in objects %}
Some value: {{ object.value }}
{% endfor %}
To learn how to create a Django application you should read the tutorial or the Django book.
A tricky way to batch select (a best practice when dealing with a lot of data).
it would be faster than calling get/get_or_create in a loop due to the fact that only one SQL query is sent.
Try to time this :
Model.objects.filter(pk__in=[list of ids])
and this :
[Model.objects.get(pk=i) for i in ids]
Also note that running on your laptop there is minimal latency when django communicates with the database (production might be different).

Categories