my checkbox html :
<form method="GET">
{% for subcategory in subcategories %}
<div class="filter-items filter-items-count">
<div class="filter-item">
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" value="{{subcategory.id}}" name="subcategory" id="{{subcategory.id}}" {% if subcategory in subcategories1 %} checked {% endif %}>
<label class="custom-control-label" for="{{subcategory.id}}">{{subcategory}}</label>
</div><!-- End .custom-checkbox -->
<span class="item-count">{{subcategory.products.all|length}}</span>
</div><!-- End .filter-item -->
</div><!-- End .filter-items -->
{% endfor %}
<button type="submit" class="btn btn-primary w-100 mt-3">Filter!</button>
</form>
it work correctly to make filter.
views :
subcatid = request.GET.getlist('subcategory')
query string:
?subcategory=5&subcategory=6
it can be one or more than one, depends on number of subcategories.
but when I go next page i suppose it become like :
?page=2&subcategory=5&subcategory=6
but it remove earliest subcategory i choose and keep the last one, just one, like :
?page=2&subcategory=5
acutely when i put Manually ?page=2&subcategory=5&subcategory=6 in url field it works but not from pagination buttons.
so while all checkboxes in filter has same names, name="subcategory" i made them unique by changing to name="{{subcategory}}", now each checkbox has unique name, now after tapping next page, Everything is kept, and there is no problem like before,
but in views, I don't know how to get them with deafferents names
subcatid = request.GET.getlist('subcategory')
You can add a QueryDict to the object that does not contain any page parameter with:
def subcategory(request, category_url):
# …
reqget = request.GET.copy()
reqget.pop('page', None)
ctx = {
# …
'reqget': reqget
}
return render(request, 'products/subcategory.html', ctx)
Then in the template, the links to go to the previous/next page will urlencode the reqget:
<a href="?page={{ products.previous_page_number }}&{{ reqget.urlencode }}" aria-label="Previous" tabindex="-1" class="page-link page-link-prev" aria-disabled="true">
Related
I'm rendering a template and it's taking anywhere between 2-3 seconds to do it. There are quite a few elements on the page (10k checkboxes which change every query) and I'm wondering if anyone knows any helpful tricks to cut down on load time.
Link to site: http://18.207.127.123/search/
FYI: there are no Django Forms or Formsets in this application! :-) I'm working with objects called Lemmas which have Forms as their children... sorry if this causes confusion.
Update #1
Whatever is taking so long, it's here:
<li class="form-item" data-lemma="{{lemma.id}}" data-group="{{group}}" style="display:none">
<input type="checkbox" name="{{lemma.name}}#{{lemma.latin}}#{{lemma.homonym_id}}#{{group}}#{{form}}" onchange="countCheckboxes(this)" id="{{lemma.id}}#{{group}}#{{form}}" checked>
<label for="{{lemma.id}}#{{group}}#{{form}}">{{ form }}</label>
</li>
are the dot methods what's slowing this down?
Update #2 - Found the bottleneck
Apparently all the {{ var }} calls for each of the 10k items is what's taking so long. Each one costs ~50-200ms.
Update #3 - Remove unnecessary tags and autoescape off
<input id="x"...
<label for="x">
can be replaced with
<label>
<input ...>
</label>
saving six variable calls.
Adding
{% autoescape off %}
# body text
{% endautoescape %}
removes the need to make each variable safe.
These two changes bring the rendering time down about 50%.
Is it database hits?
After inspecting my django connections and profiling, I'm only making two database calls as far as I can tell.
My datastructure is a dict which looks like:
results_dict = { LemmaObj1 : [form11, form12, form13, ...],
LemmaObj2 : [form21, form22, form33, ...],
...
where the LemmaObj are Queryset objects. Generating this dict only takes 0.5 seconds. I force evaluation of the lazy queryset by putting the formij into a list comprehension.
views.py
lemma_qs = Lemma.objects.prefetch_related('form_set')
# ...
# some logic goes here
# ...
for lemma in lemma_qs:
form_qs = lemma.form_set.all()
form_list = [form.name for form in form_qs if some_condition(form)]
if form_list:
results_dict[lemma] = form_list
context['results_dict'] = results_dict
return render(request, "query.html", context)
All this is to say that I'm pretty sure the slowdown isn't coming from database hits.
The template in question
In my query.html I have two for loops.
query.html
<div class="lemma-box">
<ol>
<li class="lemma-title">
<div class="lemma-col-1">
lemma [{{results_dict|length}}] (group {{group}})
</div>
<div class="lemma-col-2">
latin
</div>
<div class="lemma-col-3">
homonym id.
</div>
<div class="lemma-col-4">
<input type="button" class="pushable" value="all" onclick="checkAllLemmas('{{group}}', true)"></input>
<input type="button" class="pushable" value="none" onclick="checkAllLemmas('{{group}}', false)"></input>
</div>
</li>
{% for lemma, form_list in results_dict.items %}
<li class="lemma-item" data-lemma="{{lemma.id}}" data-group="{{group}}" onclick="activateLemma(this)">
<div class="lemma-col-1">
<input type="checkbox" onchange="countCheckboxes(this)" onclick="lemmaToggleAll(this)" id="{{lemma.id}}#{{group}}" checked></input>
<label for="{{lemma.id}}#{{group}}">
{{ lemma.name }}
</label>
</div>
<div class="lemma-col-2">
{{ lemma.latin }}
</div>
<div class="lemma-col-3">
{{ lemma.homonym_id }}
</div>
<div class="lemma-col-4">
{% with form_list|length as total %}
<span class="counter">(<span class="total">{{ total }}</span>/<span>{{ total }}</span>)</span>
{% endwith %}
</div>
</li>
{% endfor %}
{% for item in not_found_items_set %}
<li class="lemma-item-not-found">
{{ item }} not found
</li>
{% endfor %}
</ol>
</div>
<div class="form-box">
<ol>
<li class="form-title">
<div class="form-col-1">
forms (group {{group}})
</div>
<div class="form-col-2" data-group="{{group}}">
<input type="button" class="pushable" value="all" onclick="checkAllForms('{{group}}', true)"></input>
<input type="button" class="pushable" value="none" onclick="checkAllForms('{{group}}', false)"></input>
</div>
</li>
{% for lemma, form_list in results_dict.items %}
{% for form in form_list %}
<li class="form-item" data-lemma="{{lemma.id}}" data-group="{{group}}" style="display:none">
<input type="checkbox" name="{{lemma.name}}#{{lemma.latin}}#{{lemma.homonym_id}}#{{group}}#{{form}}" onchange="countCheckboxes(this)" id="{{lemma.id}}#{{group}}#{{form}}" checked>
<label for="{{lemma.id}}#{{group}}#{{form}}">{{ form }}</label>
</li>
{% endfor %}
{% endfor %}
</ol>
</div>
Is this about as fast as I'm going to get?
Jinja2 the answer?
I tried jinja2 briefly by converting query.html to query.jinja and it seemed to make almost no difference. Do I need extra steps to leverage jinja2?
Models.py
Finally, for inspection, my models.py.
models.py
class Form(models.Model):
name = models.CharField(max_length=100, db_index = True)
lemma = models.ForeignKey("Lemma", on_delete = models.CASCADE)
def __str__(self):
return self.name
class Lemma(models.Model):
name = models.CharField(max_length=100, db_index = True)
latin = models.CharField(max_length=100, db_index = True, blank = True, null = True)
homonym_id = models.IntegerField(null = True)
def __str__(self):
return self.name
One thing to do is remove as many template tags as possible by thinking clearly about what your code needs to do. That will cut down on the rendering time substantially. Such as
<input id="x"...
<label for="x">
being replaced with
<label>
<input ...>
</label>
and saving a series of template tags.
You can mark the variables as safe, which again shaves off the rendering time, such as in
{% autoescape off %}
# body text
{% endautoescape %}
The absolute best way (if you need speed and have a ton of data) is to send the JSON to the client and have the JS render it. This is a little messy, but it's much faster (2-3x faster maybe) and gives you more flexibility to manipulate the DOM piecewise.
I'm going for solution (3) for now.
How can display category and sub category in product template?
what is missing in below code
This is a part of View.py according product view, shows filtering category and subcategory from model.py.
class ProductView(View):
def get(self,request,*args,**kwargs):
categories=Categories.objects.filter(is_active=1)
categories_list=[]
for category in categories:
sub_category=SubCategories.objects.filter(is_active=1,category_id=category.id)
categories_list.append({"category":category,"sub_category":sub_category})
merchant_users=MerchantUser.objects.filter(auth_user_id__is_active=True)
return render(request,"admin_templates/product_create.html",{"categories":categories_list,"merchant_users":merchant_users})
this is part of model.py according model after migrate,
class Products(models.Model):
id=models.AutoField(primary_key=True)
url_slug=models.CharField(max_length=255)
subcategories_id=models.ForeignKey(SubCategories,on_delete=models.CASCADE)
product_name=models.CharField(max_length=255)
brand=models.CharField(max_length=255, default="")
product_max_price=models.CharField(max_length=255)
product_discount_price=models.CharField(max_length=255)
product_description=models.TextField()
productTemplate.html
<div class="col-lg-6">
<label>Category</label>
<select name="sub_category" class="form-control">
{% for category in categories %}
<optgroup label={{ category.category.title }}>
{% for sub_cat in category.sub_category %}
<option value="{{ sub_cat.id }}">{{ sub_cat.title }}</option>
{% endfor %}
</optgroup>
{% endfor %}
</select>
</div>
I implemented this solution and I will teach you step by step how to implement it. Rather, a spoiler of what we will learn:
With that in mind, let's get our hands dirty:
The first thing we will need to do is return the published categories to our template . In the file views.pyin the method of receita, where we return the receita.htmlchosen recipe to the template , which is also the page that displays the categories select, we will create a filter to return the published categories to us. And we can do this with the following code:
categorias = Receita.objects.filter(publicada=True).values('categoria').distinct('categoria')
See, through the Recipes object we filter all the recipes that have the true flag for published. After that, we just get the category values. So we say to django: - django, returns me only the recipes that have true for published, but I just want the category column . And the distinct? To serve? The distinct will guarantee us that there will be no repetition in the data. Example:
If the filter returns to us that the category column has: dessert, salty, dessert, soup, salty ... what the distinct will do is to avoid repeated data, with that we will only have: dessert, salty, soup. How are you?
In possession of that, we need to pass this value to our dictionary that will go to the context of our template:
receita_a_exibir = {
'receita': receita,
'categorias': categorias
}
Note that we pass to the 'categories' key the value of our filter that was saved in the categories variable.
Now we can move the dictionary into context:
return render(request, 'receita.html', receita_a_exibir)
Great, now we can access the categories in our template receita.html'
And to display them we can go through each category and put the name of that category in our select. Also remembering that we need to redirect the url to our search route when we click on the search button:
<div class="receipe-post-search mb-80">
<div class="container">
<form action="{% url 'buscar' %}">
<div class="row">
<div class="col-12 col-lg-5">
<select type="text" name="buscar">
{% for categoria in categorias %}
<option value="{{categoria.categoria}}">{{categoria.categoria}}</option>
{% endfor %}
</select>
</div>
<div class="col-12 col-lg-2 text-right">
<button type="submit" class="btn delicious-btn">Buscar</button>
</div>
</div>
</form>
</div>
Now, with our template ready, you will be able to see the categories in the select.
The next step is to modify our search method so that it can search by category and not just the name of the recipe as it used to.
In views.py, in the method of buscar, let's say to django: search for the name of the recipe or all the recipes in that category. We can do this through the object Q used to set up queries where we will have more than one possible answer :
lista_receitas = lista_receitas.filter(Q(nome_receita__icontains=nome_a_buscar) | Q(categoria__icontains=nome_a_buscar))
That is, filter the recipes that have that name or that have that category. And in order to Qwork, we will need to import it:
from django.db.models import Q
You can test it! With these modifications you will be able to use the select categories.
Summary of modified files:
views.py file
from django.db.models import Q
.
def buscar(request):
lista_receitas = Receita.objects.filter(publicada=True).order_by('-data_receita')
if 'buscar' in request.GET:
nome_a_buscar = request.GET['buscar']
if buscar:
lista_receitas = lista_receitas.filter(Q(nome_receita__icontains=nome_a_buscar) | Q(categoria__icontains=nome_a_buscar))
dados = {
'receitas' : lista_receitas,
}
return render(request, 'buscar.html', dados)
.
def receita(request, receita_id):
receita = get_object_or_404(Receita, pk=receita_id)
categorias = Receita.objects.filter(publicada=True).values('categoria').distinct('categoria')
receita_a_exibir = {
'receita': receita,
'categorias': categorias
}
return render(request, 'receita.html', receita_a_exibir)
.
Recipe.html file
.
<div class="receipe-post-search mb-80">
<div class="container">
<form action="{% url 'buscar' %}">
<div class="row">
<div class="col-12 col-lg-5">
<select type="text" name="buscar">
{% for categoria in categorias %}
<option value="{{categoria.categoria}}">{{categoria.categoria}}</option>
{% endfor %}
</select>
</div>
<div class="col-12 col-lg-2 text-right">
<button type="submit" class="btn delicious-btn">Buscar</button>
</div>
</div>
</form>
</div>
Some django documentation references:
Distinc
Object Q
I hope I helped you
I want to render different queryset when user click radio button without submitting the radio button input.
My models are these:
class A(models.Model):
name = models.CharField(max_length=10)
def get_a_list(self):
return self.b_set.filter(name__endswith='lorem')
def get_b_list(self):
return self.b_set.filter(name='blah')
class B(models.Model):
dept = models.ForeignKey(A, on_delete=models.CASCADE)
In template I can do something like this:
<ul>
{% for a in a.objects.all %}
{% for b in a.b_set.all %} <!-- instead of returning all I can do a.get_a_list or b.get_b_list -->
<li></li>
{% endfor %}
{% endfor %}
</ul>
If I have a group of radio buttons in the same template like this:
<div>
<input type="radio" id="a-list" value="a-list" name="filter">
<label for="a-list">a_list</label>
</div>
<div>
<input type="radio" id="b-list" value="b-list" name="filter">
<label for="b-list">b_list</label>
</div>
<div>
<input type="radio" id="all" value="all" name="filter">
<label for="all">all</label>
</div>
When user select a-list button, I want to call get_a_list, and for b-list button, get_b_list. I want to display the result without changing url.
I managed to get those different list by putting custom method in models class, but I'm lost. And I know I might lose some reputation for this question, for it might be so easy for somebody.
Any suggestion would be so grateful. Thank you in advance.
Something like:
template.html:
<div id='a_set'>
{% for a in a.objects.all %}
...
{% endfor %}
</div>
<div id='b_set'>
{% for b in a.b_set.all %}
...
{% endfor %}
</div>
script.js:
function change_sets() {
if ($('#a-list').is(':checked')) {
$('#a_set').show();
$('#b_set').hide();
}
else if ($('#b-list').is(':checked')) {
$('#a_set').hide();
$('#b_set').show();
}
}
$('#a-list').click(change_sets);
$('#b-list').click(change_sets);
Im having some problem when I want to keep a checkbox checked. When I send the form all the checkbox are checked. so idk how to change the if statement for that :(
<div class="form-group">
<label >Marca</label>
{% for brand in q %}
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="{{brand.brand}}" name="test" value="{{brand.brand}}" {% if marca %} checked="checked" {%endif%}>
<label class="custom-control-label" for="{{brand.brand}}" style="cursor: pointer;">{{brand.brand}}</label>
</div>
{% endfor %}
</div>
And here is the view:
marca = request.GET.get('test')
if marca :
products = products.filter(brand__name__in=request.GET.getlist('test'))
All the other things are fine. It shows me the brands that I choose. So I just want to keep the checkbox that I checked :( and I think the problem is that If statement in the template
Just pass a set of the values in the values to the template:
marca_vals = set(request.GET.getlist('test'))
# …
context = {
'marca_vals': marca_vals,
# …
}
return render(request, 'some_template.html', context)
in the template you can then render it with checked in case the value is in the marca_vals:
<input type="checkbox" {% if brand.brand in marca_vals %}checked{% endif %} class="custom-control-input" id="{{brand.brand}}" name="test" value="{{brand.brand}}">
You might however want to consider using a form, or even django-filters [readthedocs] to both make filtering and rendering the form more convenient.
I am working with forms sets, I was wondering how one could use
<input type='hidden'
inside a form set. (Django formsets allow us to use multiple forms instances of a single form)
Its easy to do in normal single form where you just put the field with the type='hidden' and name='fieldname' e.g.
<input type='hidden' name='user' value='{{request.user.id}}'>
Dealing with formsets is a bit catchy, how to achieve the same behavior with the forms sets?
Views.py
PictureFormSet = modelformset_factory(Picture, form=UpdatePictureForm, extra=0)
formset_qset = Picture.objects.filter(id__in=[15, 16, 17, 18, 19, 20])
if request.method == POST:
ctx['form_set'] = PictureFormSet(request.POST, queryset=formset_qset)
ctx['form_set'].save()
ctx['form_set'] = PictureFormSet(queryset=formset_qset)
return render_to_response('temp tabs.html', context_instance=RequestContext(request, ctx))
Template
<form method="POST" action="" class="form-horizontal">
{% for form in form_set %}
{{form.id}}
<div class="form-group">
<label class="col-lg-2 control-label">
{% with form.meta_data.value|load_meta_data as meta %}
<div class="portfolio-item video-container">
<a class="" href="{% url 'view_image' form.id.value %}?in=pro">
<i style="background-image: url({{ meta.image_size.thumbnail_small }});"
class="ds-thumbnail-container"></i>
</a>
</div>
{% endwith %}
</label>
<div class="col-lg-8 ">
{{ form.name }}
</div>
</div>
{% endfor %}
{{ form_set.management_form }}
{% csrf_token %}
<input type="submit" value="Submit">
</form>
Explanation
Here in this code, We are rendering images from the database for editing there names. we have url information inside the meta_data, so we have selected
fields=['id', 'meta_data', 'name']
We want to change/Update the name, but not the meta_data
This code works fine for the most part, but how i want to keep one field unchanged for the modal?
I have meta_data field that I am using in the template, but i do not want that field to be modified, that value should be in the form like this
{{form.meta_data}}
This turns it into text area, with different name and id. and it expects it be changing. but i want to declare a hidden field and sets its value to the form.meta_data.value
If you have any questions regarding this please do not hesitate to ask.
Thanks.