I currently have a template containing an html form, with the lines:
{% for r in q1.responseoption_set.all %}
<span class="r"><input type="{{ q1.answer_type }}" name="r{{ r.id }}" id="r{{ forloop.counter }}"/>
<label {% if q1.answer_type == "text" %}class="textanswer"{% endif %}for="r{{ forloop.counter }}">{{ r.text }}</label></span><br>
{% endfor %}
problem is, because they don't all have the same name (that's why, right?), if I pick a radio button, and then switch to another one, the first one still shows as selected.
However, at the moment, I need them to all have different names because I need to be able to identify the choices within my view, and as far as I can tell, all I can get from the request is [name, value], e.g. [r200, "on"]
The only way around this that I can think of is to insert a script that assigns a check event to each button, and then, once checked, inserts a hidden input with the name I want, but that seems messy.
SO, is there a way for me to either:
get the button id from the request OR have the buttons "refresh" somehow as they are.
Keep the name the same, and set the value for each input choice to the answer id.
{% for r in q1.responseoption_set.all %}
<span class="r"><input type="{{ q1.answer_type }}" name="{% questionId %}" value="r{{ r.id }}" id="r{{ forloop.counter }}"/>
<label {% if q1.answer_type == "text" %}class="textanswer"{% endif %}for="r{{ forloop.counter }}">{{ r.text }}</label></span><br>
{% endfor %}
Related
I have a checkboxselectmultiple on an m2m model field in an ModelForm that is required - meaning at least one of the choices must be selected. I am using the boostrap5 was-validated class on my form:
<form method="POST" action="{{ request.path }}" {% if attempt_submit %}class="was-validated"{% endif %}>
This question is about how the validation shows up on my form with bootstrap5. Should be red border and red ! if not validated, green border and checkmark if so. However, for my checkboxes, if I don't have any selected (and everything else on the form validates), the form will show each checkbox option as green instead of red. Yet, it does know that it's invalid because the page focus will come back up the checkbox area to show the user what to correct (and it doesn't pass form.is_valid() in views.py.
Why are these labels and boxes still showing green and how can I show them as red until I select one and it's now valid?
Along the lines of this post, I have tried adding
{% if form.sales_location.field.required %}required{% else %}form.sales_location.field.required=""{% endif %}
to the checkbox <input>, but then each field is required and if I select one, the other remaining options still remain red - as if every option would have to be selected for the form to validate. Am I supposed to do this anyway and then add something else (JS?) to disable that?
Not sure exactly what code would be helpful to see...
in models.py, this is the field:
sales_location = models.ManyToManyField(SalesLocation, verbose_name="Where do you sell your products? (select all that apply)" )
in forms.py
model = AssessmentProfile
fields = [
'sales_location',
...
]
widgets = {
'sales_location': forms.CheckboxSelectMultiple(attrs={
'class': 'form-check',}),
}
I add this because I read this post about making sure that I use a `ModelMultipleChoiceField' - but I assume that is already happening because it's a model form.(?)
Probably most important, in the template thisform.html, here's how I'm manually adding this form element:
<div class="field-wrapper">
{{ form.sales_location.label_tag }}
<ul id="id_sales_location" class="form-check">
{% for pk, choice in form.sales_location.field.widget.choices %}
<li>
<input {% for location in location_qs %}{% if location == pk %}checked='checked'{% endif %}{% endfor %}
name="sales_location" class="form-check-input" type="checkbox" value="{{ pk }}" id="id_sales_location_{{forloop.counter0}}"
{% if already_submitted %}disabled="disabled"{% endif %}>
<label class="form-check-label" for="id_sales_location_{{forloop.counter0}}">
{{ choice }}
</label>
</li>
{% endfor %}
</ul>
</div>
Also, I tried updating css to manually format red, but think that doesn't address the root of the problem, plus, I wasn't able to do it successfully anyway.
Thanks for taking a look and for any suggestions.
In the end, I used javascript to solve this problem.
I updated the form template
<div class="field-wrapper">
{{ form.sales_location.label_tag }}
<ul id="id_sales_location" class="form-check">
{% for pk, choice in form.sales_location.field.widget.choices %}
<li>
<input {% for location in location_qs %}{% if location == pk %}checked='checked'{% endif %}{% endfor %}
name="sales_location" class="form-check-input" type="checkbox" value="{{ pk }}" id="id_sales_location_{{forloop.counter0}}"
{% if not form.sales_location.field.required %} {% else %} required {% endif %}
{% if already_submitted %}disabled="disabled"{% endif %}>
<label class="form-check-label" for="id_sales_location_{{forloop.counter0}}">
{{ choice }}
</label>
</li>
{% endfor %}
</ul>
</div>
to add required to the input if the checkbox is required. This allows all the checkboxes to come up red when validating, if the field is empty.
Then, I added this javascript to remove 'required' if it's checked.
<script>
// Select all checkboxes using querySelectorAll.
var checkboxes = document.querySelectorAll("input[type=checkbox][name=sales_location]");
checkboxes.forEach(function(checkbox) {
checkbox.addEventListener('change', function() {
for (var cb of checkboxes) {
cb.removeAttribute('required');
}
})
});
</script>
If the field is not required, nothing changes. But if it is, then the required attribute on the <input>is gone and all the checkboxes show up green, which is what I wanted.
It's not perfect because if the checkboxes become unchecked, they don't change back to red. So I am making a dirty assumption that if someone checked a box, they wouldn't go back and uncheck it and try to submit. In which case, the validation would show green (and unchecked) until Submit was pressed again, but then it would take them back to this field which would be red again. If you know how to improve my code by adding the different case for the change function (only if the field is required), please feel free to add that. Cheers.
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 %}
I have a form with next field:
phone_code = forms.ChoiceField(
label=_('Phone code'),
widget=forms.Select(attrs={'class': 'short'})
)
I need to add data-attribute to <option> to store country code for each phone code. I'm trying to do it in the next way: in template I render this field the next way:
<select name="{{ phone_code_field.name }}">
{% for choice in phone_code_field.field.extra_data %}
<option data-choice-code="{{ choice.0.0 }}" value="{{ choice.0.1 }}">{{ choice.1 }}</option>
{% endfor %}
</select>
extra_data is a data-structure where
I store phone code and country code for each choice.
Question: But after error validation the chosen value of phone-code is the first choice in the selector, not the last selected choice. If field is rendered automatically by django forms({{ form.phone_code }}), it works correct, but in this case I have to override Select widget. How can I implement this in the way I do?
Or how can I define in template which attribute should be selected?
If you insist on rendering the field manually, then you also have to determine manually which item is selected, and output the selected attribute on the relevant one.
{% for choice in phone_code_field.field.extra_data %}
<option {% if phone_code_field.field.value == choice.0.1 %}selected="selected"{% endif %} data-choice-code="{{ choice.0.0 }}" value="{{ choice.0.1 }}">{{ choice.1 }}</option>
{% endfor %}
It is great that Django 1.4 allows fine graining of radio select
{% for radio in form.important_client reversed%}
{{radio.tag}}<label for="????">{{radio.choice_label}}</label>
{% endfor %}
but for some odd reason when using this methodology, the <input> have no IDs. And hence I can't set the <label for='ID' /> accordingly. That causes big issues in my CSS.
Is there anyway to get the IDs set nonetheless?
While debuging a RadioSelect rendering, I got no idea of using radio tag and label elegantly. So here is my attempt to solve your problem:
{% for radio in form.important_client reversed %}
<input name="{{ radio.name }}" type="radio" id="radio_{{ radio.index }}" value={{ radio.choice_value }}>
<label for="radio_{{ radio.index }}">{{ radio.choice_label }}</label>
{% endfor %}
Instead of radio.index property, which is not documented, you can use forloop.counter.
Just in case I attach a screenshot of debug window, where example of radio context is shown (form_of_field variable on a figure):
This is one way to do that, it might not be the best but it works. In your form you can set the id for each of the choices like this:
from django import forms
class MyForm(forms.Form):
CHOICES = (('1','Available'),('2','Not Available'))
input = forms.ChoiceField(widget=RadioSelect(attrs={'id' : 'myId'},choices=CHOICES)
Then on your template:
{% for radio in form.input %}
{{ radio }}
{% endfor %}
And your HTML will look like this:
<label for="myId_0"><input id="myId_0" name="input" type="radio" value="1"></label> Available
<label for="myId_0"><input id="myId_0" name="input" type="radio" value="2"></label> Not Available
I hope this works!
I want to customize the layout of forms in a formset (that is, I don't want to use .as_table() or .as_p() and the like). I'm trying to get the name of a form field for use in its label's for attribute, but I'm not sure how to go about it. I'm hoping that I won't need to construct a new name/ID for the field from scratch. Here's an example of what I'm working with right now:
{% for form in formset.forms %}
<!-- The field for the "path" form field -->
<label for="{{what do I put here?}}">{{form.fields.path.label}}:</label><input type="text" id="{{django creates this one; do I have to do my own with the for loop counter or something?}}" name="{{probably the same as id}}" />
{% endfor %}
Is there any sort of "create ID for formset field" sort of method?
This is likely what you want.
for="{{ form.your_field.html_name }}"
First, you want to use the form element's id, instead of name.
I tried Django 1.3 Alpha-1 and the following worked:
{% for form in formset.forms %}
<label for="{{ form.my_field.auto_id }}">{{ form.my_field.label }}</label>
{{ form.my_field }}
{% endfor %}
Enjoy!