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.
Related
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">
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.
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);
Currently, I have almost two exact same templates and they use the same Django form, but there is only 1 parameter that changes in these two forms which is the action method, that is,
Django form
class DropDownMenu(forms.Form):
week = forms.ChoiceField(choices=[(x,x) for x in range(1,53)]
year = forms.ChoiceField(choices=[(x,x) for x in range(2015,2030)]
template 1
<form id="search_dates" method="POST" action="/tickets_per_day/">
<div class="row">
<div style="display:inline-block">
<h6>Select year</h6>
<select name="select_year">
<option value={{form.year}}></option>
</select>
</div>
<button type="submit">Search</button>
</div>
</form>
template 2
<form id="search_dates" method="POST" action="/quantitative_analysis/">
<div class="row">
<div style="display:inline-block">
<h6>Select year</h6>
<select name="select_year">
<option value={{form.year}}></option>
</select>
</div>
<button type="submit">Search</button>
</div>
</form>
The only thing that varies is the action method, so I would like to know if it is possible to re-use one template that varies only in the action method. If it is possible, can you help me with the code?
I checked this question django - how to reuse a template for nearly identical models? but I am not using any model here with my templates.
Of course there is a way. {% include %} to the rescue!
Create a base template for your form, like this:
<!-- form_template.html -->
<form id="search_dates" method="POST" action="{{ action }}">
<div class="row">
<div style="display:inline-block">
<h6>Select year</h6>
<select name="select_year">
<option value={{form.year}}></option>
</select>
</div>
<button type="submit">Search</button>
</div>
</form>
Notice the placeholder action. We'll need it in the next step.
Now, you can reuse this template, by simply writing:
<!-- a_template.html -->
{% include 'form_template.html' with action='/tickets_per_day/' %}
<!-- b_template.html -->
{% include 'form_template.html' with action='/quantitative_analysis/' %}
Well from your views you can pass action in the context and use that in template in this way you don't have to create two separate templates. Lets say the template name is abc.html used by two views:
def my_view_a(request):
ctx = {'action': '/tickets_per_day/'}
return render(request, 'abc.html', ctx)
def my_view_b(request):
ctx = {'action': '/quantitative_analysis/'}
return render(request, 'abc.html', ctx)
Then in template you would simply do:
<form id="search_dates" method="POST" action="{{ action }}">
In the above code the action is hardcoded better to use reverse to resolve url path by name:
ctx = {'action': reverse('namespace:url_name')} # replace namespace and url_name with actual values
Use this in template2:
{% include "template1.html" with form=form %}
It will work.
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.