I have a Profile Object which has a many to many reationship with a Hobbies object. A profile is used for creating a User. Each User can also have a load of hobbies that are predefined. What I want to do is let Users pick some hobbies they are interested in. However, I am not sure how to display all these Hobbies and let Users pick them on the actual display. Here is the code:
TYPES = (
("Football", "Football"),
("Cricket", "Cricket"),
("Swimming", "Swimming"),
("Cycling", "Cycling")
)
class Hobby(models.Model):
myfield = MultiSelectField(choices = TYPES)
And the User :
class Profile(models.Model):
email = models.CharField(max_length=200)
hobbies = models.ManyToManyField(Hobby)
And the HTML code I use is here:
<span class="fieldname">Hobbies</span>
{% for hobby in all_hobbies %}
<input type="checkbox" name={{hobby}} value={{hobby}}> {{hobby}}<br>
{% endfor %}
However this only displays
What I want it to display is all the hobbies with the choices, instead of the whole Object. I am not sure how to do this and would appreciate any help.
Try doing something like
<span class="fieldname">Hobbies</span>
{% for hobby in all_hobbies %}
<input type="checkbox" name="{{ hobby.myfield }}" value="{{ hobby.myfield }}"> {{ hobby.myfield }}<br />
{% endfor %}
Under Hobby class, you should add the str method that will display the value of your hobby instead of 'Object'
def __str__(self):
return self.myfield
You can also use __unicode__ instead of __str__ if you are with Python 2.x
Edit:
After I read again your question, I understood that your problem was specifically not having several checkboxes for the multiple choices you have, here is how you can show all the choices, you should pass the hobbies field as a context :
{% for value, text in form.hobbies.field.choices %}
<div class="ui slider checkbox">
<input id="id_hobbies_{{ forloop.counter0 }}" name="{{ form.hobbies.name }}" type="checkbox" value="{{ value }}"{% if value in checked_hobbies %} checked="checked"{% endif %}>
<label>{{ text }}</label>
</div>
{% endfor %}
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.
I'm new to Django and trying to solve a simple problem. I looked through many similar questions but still is not able to find the solution.
In my template I have a few checkboxes which are built according to records in the database.
My Model is:
class Status(models.Model):
statusCode=models.IntegerField()
definition = models.CharField(max_length=30)
def __str__(self):
return f"{self.definition}"
In my view I'm getting the values of checkboxes that user chose and store them in the list:
if request.method == "POST":
status_list = request.POST.getlist('statuses')
I pass this list as a context:
return render(request, 'search/index.html',{"chosen_statuses": status_list,
"statuses": Status.objects.all()}
Then I want to display results of the query keeping the checkboxes that user selected - selected.
This is my code in template:
<div>
<label for="status">Include statuses: </label>
{% for status in statuses %}
<input type='checkbox' value='{{ status.id }}' name='statuses'
{% if status.id in chosen_statuses %}
checked
{% endif %}>
<label for='{{ status }}'>{{ status }}</label>
{% endfor %}
</div>
The list "chosen_values" contains the user's choices as needed, and the results of the query are correct. But the checkboxes are left unchecked.
I would appreciate some help.
Well, after hours, probably this a newbee mistake.
It turned out I cannot pass list to template to compare its values to query object.
In my views I should pass the query object instead (which takes list as a parameter in filter:
"chosen_statuses": Status.objects.filter(statusCode__in = status_list)
and then I adjust if statement in template:
<div>
<label for="status">Include statuses: </label>
{% for status in statuses %}
<input type='checkbox' value='{{ status.id }}' name='statuses'
{% if status in chosen_statuses %}
checked
{% endif %}>
<label for='{{ status }}'>{{ status }}</label>
{% endfor %}
</div>
I'm creating a panel where a user can assign an assignment to other users. I'm attempting to build this with the ModelMultipleChoiceField and a form called AssignedUsersForm
forms.py
class AssignedUsersForm(ModelForm):
class Meta:
model = Assignment
fields = ['users']
custom_users = CustomUser.objects.all()
users = forms.ModelMultipleChoiceField(queryset=custom_users, widget=forms.CheckboxSelectMultiple())
template
<form method="post" id="assigned_users_form">
{% csrf_token %}
{{ assigned_users_form.errors }}
{{ assigned_users_form.non_field_errors }}
<div class="fieldWrapper">
{{ assigned_users_form.content.errors }}
{% for user in assigned_users_form.users %}
<div class="myradio">
{{ user }}
</div>
{% endfor %}
<br>
<div style="text-align:center;">
<input class="input-btn" type="submit" value="Save" name='submit_assigned_users_form'>
</div>
</form>
I've successfully rendered each of the options for CheckboxSelectMultiple individually in my HTML.
Unfortunately, each iteration of 'user' renders CustomUser object (#) - the default name for the object.
From what I understand, the Checkbox widget does not pass the original queryset to the template. Thus, I'm unable to access the model attributes of each user and render information such as name, profile picture, etc.
Is there a method of accessing the actual object that's represented in the checklist, instead of the _ str _ value?
I've considered running a parallel iteration of queryset in the template, but I'm not sure if that's possible in Django.
I've also considered creating custom template tags to access each object based on the id in str, but that seems like overkill?
Is there a more intuitive method that I'm missing here?
I found a work around by creating a template_filter that takes the checkbox value and returns the corresponding object to the template.
template_tags.py
#register.filter(name='get_user')
def get_user(user_id):
user = CustomUser.objects.get(pk=user_id.value)
return user
template
<form method="post" id="assigned_users_form">
{% csrf_token %}
{{ assigned_users_form.errors }}
{{ assigned_users_form.non_field_errors }}
<div class="fieldWrapper">
{{ assigned_users_form.content.errors }}
{% for user in assigned_users_form.users %}
<div class="myradio">
{% with customuser=user.data.value|get_user %}
{{user.tag}} {{customuser.first_name }}
{% endwith %}
</div>
{% endfor %}
<br>
<div style="text-align:center;">
<input class="input-btn" type="submit" value="Save" name='submit_assigned_users_form'>
</div>
</form>
Essentially, this code looks at the checkbox widget iteration (user) and gets the value, passes that value to the template tag and then returns the associated object back to the page.
Not the best solution given that you're repeating the query search for each object, rather than gathering them all in one go. But this is the best I could come up with.
I'm begginer of django and i try to implement search engine to look for recipes that contains selected ingredients.
I want to do form with all ingredients_type from my Model and user can select up to 3 of them (checkbox prefered). Then i want to filter recepe that contains them.
I was looking for generic form but never get right result.
Right now i have only a scratch and Im looking for any advice
model.py
class IngredientsType(models.Model):
type = models.CharField(max_length=60, blank=True)
def __str__(self):
return self.type
search.html
{% for ingredients in ingredients_type %}
<input type="checkbox" id="{{ingredients.type}}">
<label for="{{ingredients.type}}">{{ingredients.type}}</label>
{% endfor %}
<form method="POST" action="#">
<button type="submit" name="save" value="save">szukaj</button>
</form>
Do i have to create custom form or there is good way to use some of generic form?
I think what you are on the right path, but there are some mistakes in your code.
search.html
<form method="POST" action="">
{% csrf_token %}
{% for ingredients in ingredients_type %}
<input type="checkbox" id="{{ingredients.type}}" value="1">
<label for="{{ingredients.type}}">{{ingredients.type}}</label>
{% endfor %}
<button type="submit" name="save" value="save">szukaj</button>
</form>
model.py
You might want to use many-to-many relationship to represent to relation between the ingredient types and the recipes.
views.py
Make sure you have the POST handling method to work with your filter query. This should get data from the form.
In a Django template, I'm displaying some usernames (or unames) along with check boxes like so:
<form method="POST" action="{% url 'process_unames' %}">
{% csrf_token %}
{% for name in unames %}
<input type="checkbox" name="target{{ forloop.counter0 }}" value="{{ unames|index:forloop.counter0 }}" checked>{{ name }}<br>
{% endfor %}
<button name="duration" value="{{ value }}" type="submit">Submit</button>
</form>
And then in the function process_unames, I'm trying to retrieve all checked unames via:
def process_unames(request, *args, **kwargs):
uname_list = request.POST.getlist('unames')
This is consistently yielding an empty list, regardless of which uname I check or uncheck. How do I fix this? An illustrative example would be great.
In case it matters, I haven't declared any form in forms.py with this Django template. Moreover, note that index is a custom template tag which does the following (and I can vouch it's correctly working):
from django.template.defaulttags import register
#register.filter(name='index')
def index(List, i):
return List[int(i)]
You are checking the wrong variable. You access the input by its name.
So for a single value you would retrieve the data for a form like this
<input type="checkbox" name="user_name" value={{uname}}>
via
request.POST.get('user_name')
See: https://docs.djangoproject.com/en/1.10/topics/forms/#the-work-that-needs-to-be-done
And if you have loop, you still give each relevant input the same name and use request.POST.getlist() to access the values as a list.
See: https://stackoverflow.com/a/23470119/630877
You don't have any fields called "unames" in the template. You have a bunch of unrelated checkboxes called "targetX" where X is a number. Replace that with just name="unames" and your code will work.
Note two things: firstly, you should be using Django's forms framework; and secondly, your filter is pointless because {{ name }} would already by the value in unames, since that is what you are iterating over (and, indeed, you are already using that as the label).
{% for name in unames %}
<input type="checkbox" name="unames" value="{{ name }}" checked>{{ name }}<br>
{% endfor %}