Multiple checkboxes validation rules, only works when every checkbox selected - django

I have a template which goes through a queryset and creates a checkbox for each item which seems to be a validation problem. I can only submit this when I check every checkbox and I just can't seem to figure out what is wrong.
my template:
<form method="POST">
{% csrf_token %}
<fieldset>
{% for choice in choices %}
{{ choice.description }}
<input type="checkbox" value="{{choice.section}}" name="sections" required="">
{% endfor %}
<button type="submit">Submit</button>
</fieldset>
</form>
my forms.py
class AddSectionForm(forms.Form):
sections = forms.MultipleChoiceField(
required=False, widget=forms.CheckboxSelectMultiple())
edit
Urghh, I'm an idiot, it's the required="" in the html checkbox object!

Thanks Willem, you got it! Also needed to change the checkbox values for the primary keys.
forms.py:
class AddSectionForm(forms.Form):
sections = forms.MultipleChoiceField(
required=True, widget=forms.CheckboxSelectMultiple(),
choices=Section.objects.all().values_list())
template:
<form method="POST">
{% csrf_token %}
<fieldset>
{% for choice in choices %}
{{ choice.description }}
<input type="checkbox" value="{{choice.pk}}" name="sections">
{% endfor %}
<button type="submit">Submit</button>
</fieldset>

Your checkboxes should not have required="", otherwise it is required to check these. Furthermore it is probably more elegant to use a ModelMultipleChoiceField:
class AddSectionForm(forms.Form):
sections = forms.ModelMultipleChoiceField(
queryset=Section.objects.all(),
required=True,
widget=forms.CheckboxSelectMultiple()
)

Related

Django BooleanField - Checkbox not showing in form on website

I have the following:
forms.py
class StoreSettingsForm(ModelForm):
enable_repricer = forms.BooleanField(required=True)
enable_ignore_min_price = forms.BooleanField(required=True)
class Meta:
model = Store
fields = '__all__'
exclude = ['user']
models.py
class Store(models.Model):
user = models.ForeignKey(User, models.SET_NULL, blank=True, null=True)
store_name = models.CharField(max_length=100, blank=False)
...
enable_repricer = models.BooleanField(default=False)
enable_ignore_min_price = models.BooleanField(default=False)
template.html
<form method="post">
{% csrf_token %}
<h4>Repricer Settings</h4>
<div class="row">
{{ form.non_field_errors }}
<div class="fieldWrapper col s4">
{{ form.enable_repricer.errors }}
<label for="{{ form.enable_repricer.id_for_label }}">Enable Repricer:</label>
{{ form.enable_repricer }}
</div>
<div class="fieldWrapper col s4">
{{ form.enable_ignore_min_price.errors }}
<label for="{{ form.enable_ignore_min_price.id_for_label }}">Allow Repricer to ignore min_price:</label>
{{ form.enable_ignore_min_price }}
</div>
<div class="fieldWrapper col s4">
{{ form.repricer_factor.errors }}
<label for="{{ form.repricer_factor.id_for_label }}">Repricer aggressiveness factor (1-100):</label>
{{ form.repricer_factor }}
</div>
</div>
<input class="btn" type="submit" value="Submit">
</form>
</div>
view.py
class StoreSettingsView(View):
template_name = 'store_settings.html'
def get(self, *args, **kwargs):
store = Store.objects.get(id=kwargs['id'])
data = {
'store_name': store.store_name,
'store_email': store.store_email,
...
'enable_ignore_min_price': store.enable_ignore_min_price,
'enable_repricer': store.enable_repricer,
'repricer_factor': store.repricer_factor,
}
form = StoreSettingsForm(initial=data)
return render(self.request, self.template_name, {
"store": store,
"form": form,
})
It does not show up in the form. All field are showing on the page but not the 2 boolean fields. The labels are showing and in the HTML.
I have excluded many fields from the code blocks to make it more easy to read.
I was with this problem and I solved it doing that:
#----- My error was caused by Materialize css ----#
Not showing checkbox
<form>
{% for field in form %}
{{field.label_tag}}<br>
{{field}}
<br>
<br>
{% endfor %}
</form>
Using Materialize, You must have a span tag in your label:
Example in Materialize doc:
<label>
<input type="checkbox" class="filled-in" checked="checked" />
<span>Filled in</span>
</label>
I solved the problem refacting to the Materialize's doc structure:
<form>
{% for field in form %}
<label>
{% if field.label == "colorida" %} <!-- 'colorida' is my boolean field -->
{{field}}
<span>{{field.label}}</span> <!-- if boolean field, span must be after input (in materialize) -->
{% else %}
<span>{{field.label}}</span> <!-- description before input if not boolean field -->
{{field}}
{% endif %}
</label>
<br>
<br>
{% endfor %}
</form>
Test your example on a page without any CSS, the problem probably is caused by CSS
Ok. When typing the question something arose to me. What if.... and yes that was it. In some way my CSS is disabling the checkbox and i really do not know why. When removing the CSS it works fine. Sorry.

Django - Account Settings BooleanField

I reviewed most of the posts here related to Django and BooleanField update, but I couldn't find any related to my problem solution.
I have a custom user models.py:
# Own custom user model
class Account(AbstractBaseUser):
username = models.CharField(max_length=30, unique=True)
guardianSource = models.BooleanField(default=True)
bbcSource = models.BooleanField(default=False)
independentSource = models.BooleanField(default=False)
categoryCoronaVirus = models.BooleanField(default=False)
categoryPolitics = models.BooleanField(default=False)
categorySport = models.BooleanField(default=False)
When I register a user it seems to register in the database all correct values for the checkboxes. The problem is when I want to edit user information I cannot see if a checkbox was checked on registering or not, it displays the checkboxes itself, but they are all empty (False). However, it correctly requests the username and displays it so I can edit it, but all the checkboxes are unchecked.
views.py:
def account_view(request):
if not request.user.is_authenticated:
return redirect('login')
context = {}
if request.POST:
form = AccountUpdateForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
context['success_message'] = "Updated"
else: # Display the saved user details from database
form = AccountUpdateForm(
initial = {
'username':request.user.username,
"guardianSource": request.user.guardianSource,
"bbcSource": request.user.bbcSource,
"independentSource": request.user.independentSource,
"categoryCoronaVirus": request.user.categoryCoronaVirus,
"categoryPolitics": request.user.categoryPolitics,
"categorySport": request.user.categorySport,
})
context['account_form'] = form
return render(request, 'accounts/account.html', context)
account html:
{% extends 'base.html' %}
{% block content %}
<form class="form-signin" method="POST">{% csrf_token %}
<h1 class="h3 mb-3 font-weight-normal">Account Settings</h1>
<label for="username" class="sr-only">Username</label>
<input type="text" name="username" id="username" class="form-control" placeholder="Username" value="{{account_form.initial.username}}">
<br>
<div class="form-control">
<p><b>Please choose news sources!</b></p>
<label for="guardianSource" >The Guardian</label>
<input type="checkbox" name="guardianSource" id="guardianSource" value="{{account_form.initial.guardianSource}}" >
<br>
<label for="bbcSource" >BBC News</label>
<input type="checkbox" name="bbcSource" id="bbcSource" value="{{account_form.initial.bbcSource}}" >
<br>
<label for="independentSource" >The Independent</label>
<input type="checkbox" name="independentSource" id="independentSource" value="{{account_form.initial.independentSource}}" >
</div>
<br>
<div class="form-control">
<p><b>Please choose news category!</b></p>
<label for="categoryCoronaVirus" >The Guardian</label>
<input type="checkbox" name="categoryCoronaVirus" id="categoryCoronaVirus" value="{{account_form.initial.categoryCoronaVirus}}" >
<br>
<label for="categoryPolitics" >BBC News</label>
<input type="checkbox" name="categoryPolitics" id="categoryPolitics" value="{{account_form.initial.categoryPolitics}}" >
<br>
<label for="categorySport" >The Independent</label>
<input type="checkbox" name="categorySport" id="categorySport" value="{{account_form.initial.categorySport}}">
</div>
{% for field in registration_form %}
<p>
{% for error in field.errors %}
<p sttle="color:red"> {{ error }}</p>
{% endfor %}
</p>
{% endfor %}
{% if registration_form.non_field_errors %}
<div style="color:red;">
<p>{{registration_form.non_field_errors}}</p>
</div>
{% endif %}
{% for field in account_form %}
<p>
{% for error in field.errors %}
<p sttle="color:red"> {{ error }}</p>
{% endfor %}
</p>
{% endfor %}
{% if account_form.non_field_errors %}
<div style="color:red;">
<p>{{account_form.non_field_errors}}</p>
</div>
{% endif %}
{% if success_message %}
<p style = "color: green; text-align:center;">{{success_message}}</p>
{% endif %}
<h6 class="text-muted">
If you don't choose a source and category it will automatically assign the ones that are checked!<br>
NOTE: You <b>MUST</b> select at least 1 choice for each!!!
</h6>
<button class="btn btn-lg btn-primary btn-block" type="submit">Save Changes</button>
</form>
<div class="d-flex flex-column">
<a class="m-auto" href="{% url 'password_change' %}">Change password</a>
</div>
{% endblock content %}
Thanks in advance and I am sorry if my post is duplicated.
EDIT:
The issue was found.
In the account.html file the values for each input type checkbox were wrong.
What I changed from:
<input type="checkbox" name="guardianSource" id="guardianSource" value="{{account_form.initial.guardianSource}}" >
To:
<input type="checkbox" name="guardianSource" id="guardianSource" value="{{account_form.guardianSource}}" >
For all the input values after the username input the initial had to be removed
You don't need to pass initial when you are initializing a ModelForm and have the instance
else: # Display the saved user details from database
form = AccountUpdateForm(instance=request.user)
You can use the field directly in the template and it will be rendered with the correct values, you don't need to construct the html for the input yourself
<label for="{{ account_form.guardianSource.id_for_label }}">The Guardian</label>
{{ account_form.guardianSource }}
The docs have a section on how to render fields manually

Django - Select a valid choice. That choice is not one of the available choices

Here are my forms:
class RoleForm(forms.ModelForm):
class Meta:
model = models.RoleModel
fields = ['name']
class FeatureForm(forms.ModelForm):
role = forms.ModelChoiceField(queryset=models.RoleModel.objects.values_list('name', flat=True).distinct())
class Meta:
model = models.FeatureModel
fields = ['role','feature']
In my bootstrap form, the choices display properly. I get a list of all roles. But if I fill the feature form and hit submit it says - "Select a valid choice. That choice is not one of the available choices."
My models are:
class RoleModel(models.Model):
name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)
class FeatureModel(models.Model):
role = models.ForeignKey(RoleModel, on_delete=models.PROTECT)
feature = models.CharField(validators=[alphanumeric], max_length=10, unique=True)
my bootsrtap form is:
<form action="{% url 'feature_form' %}" novalidate method="POST">{% csrf_token %}
<div class="row">
<div class="col">
<label for="role">{{ fform.role.label }}</label>
<p><select class="form-control id="role" name="role">
{% for item in fform.role %}
{{ item }}
{% endfor %}
</select></p>
{% for error in fform.role.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
<div class="col">
<label for="feature">{{ fform.feature.label }</label>
<p><input type="text" class="form-control" id="feature" name="feature"
{% if fform.feature.value is not None %}
value="{{ fform.feature.value }}"
{% endif %}
></p>
{% for error in fform.feature.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
</div>
<input class='btn btn-primary btn-sm' type='submit' value='Save'>
</form>
My need is simple. The second form (FeatureForm) has two fields. role being foreign key of another model and a text field to type in name of a feature. On my client side, I need the foreign key to be displayed as a select option with a list. I chose a value from this select, enter the value of feature and hit save. It has to save.
It doesn't work, because your queryset includes only names, but you need an id / primary key of a RoleModel. Since your choices don't have an id, they aren't a valid choice.
Firstly, your RoleModel name is set to unique and is therefore no point in querying distinct() name values because they will be distinct already by unique definition.
You also don't need to construct your own select input. Django will take care of this.
All you need is:
class RoleModel(models.Model):
name = models.CharField(validators=[alphanumeric], max_length=50, unique=True, blank=False)
def __str__(self):
return self.name
class FeatureForm(forms.ModelForm):
class Meta:
model = models.FeatureModel
fields = ['role', 'feature']
def __init__(self, *args, **kwargs):
super(FeatureForm, self).__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].widget.attrs = {'class': 'form-control'}
and
<form action="{% url 'feature_form' %}" novalidate method="POST">
{% csrf_token %}
<div class="row">
<div class="col">
<label for="role">{{ fform.role.label }}</label>
<p>{{ fform.role }} </p>
{% for error in fform.role.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
<div class="col">
<label for="feature">{{ fform.feature.label }</label>
<p>{{ fform.feature }}</p>
{% for error in fform.feature.errors %}
<p><small class="alert-danger">{{ error }}</small></p>
{% endfor %}
</div>
</div>
<input class='btn btn-primary btn-sm' type='submit' value='Save'>
</form>
EDIT: Otherwise, a Select input in this case should be constructed as:
<select>
<option value="1">Name 1</option>
<option value="2">Name 2</option>
</select>
where value is an id, a primary key of RoleModel in your case. Your select options don't have this value.

Why aren't labels appearing in my Django ModelForm?

I have a very simple ModelForm in my app that looks like this:
# ModelForm
class ProductForm(ModelForm):
class Meta:
model = MyModel
exclude = ['created', 'last_modified', 'serial_number']
# Model
class BaseModel(models.Model):
created = models.DateTimeField(auto_now_add=True, blank=True, null=True)
last_modified = models.DateTimeField(auto_now=True, blank=True, null=True)
class MyModel(BaseModel):
product = models.TextField(verbose_name='Product Name')
serial_number = models.TextField(verbose_name='Serial Number')
And a form that looks like this:
# Form
<form method="POST" action="{% url some_url %}">
{% csrf_token %}
{{ formset.management_form }}
{% for form in formset %}
{% for field in form %}
{% if field.errors %}
<div>{{ field.errors }}</div>
{% endif %}
<div>
{{ field.label_tag }}:
{{ field }}
</div>
{% endfor %}
{% endfor %}
<div class="actions">
<input class="button submit focus" type="submit" value="{% trans "Save" %}" />
</div>
</form>
When I check out the view using this, I just see a colon (:) followed by the textfield: The label is gone.
According to the documentation for ModelForm:
In addition, each generated form field has attributes set as follows:
...
The form field’s label is set to the verbose_name of the model field, with the first character capitalized.
What mistake have I made?
I am using Django 1.4.1 if it matters.
You have to put the field label inside a <label> tag. So:
<div>
<label for="id_{{field.html_name}}">{{field.label}}:</label>
{{ field }}
</div>
The only solution I managed to find that still allowed me to separate each row of the form was doing the following:
<form method="POST" action="{% url some_url %}">
{% csrf_token %}
{{ formset.as_ul }}
<div class="actions">
<input class="button submit focus" type="submit" value="{% trans "Save" %}" />
</div>
</form>
... the key piece being the {{ formset.as_ul }} instead of iterating through each field.
As for why the other listed solution (or the solution in the documentation) didn't work, I will remain baffled.

How do I customize Django's comments form?

I am trying to customize Django's comments form. Inside django.contrib.comments.forms I noticed that all the field forms are declared in the class CommentDetailForm, which is inherited from CommentSecurityForm. Then I think when I write the template tag {% get_comment_form for order as form %}, it's getting the class called CommentForm which inherits CommentDetailForm with a honeypot field.
I wanted to customize the comments form so that it only displays the comments field (and not the optional name, email, or URL fields). Those information will be given by the current logged in user. In fact, only logged in users with certain UserProfile.user_type (UserProfile has a foreign key to User) are allow to comment.
Any tips on how to achieve this? Looking at the source code of the Django's comments already scares me lol.
EDIT:
Here is how the comment template looks so far:
{% get_comment_form for order as form %}
<form action = "{% comment_form_target %}" method = "post">
{% csrf_token %}
{{ form }}
<input type = "submit" name = "submit" value = "Post">
</form>
And the site looks like this
I want to hide Name, Email address, and URL.
You should be able to do all of this in the template:
{% ifequal User.profile.user_type "comment_type" %}
{% get_comment_form for order as form %}
<form action="{% comment_form_target %}" method="post">
{% csrf_token %}
{% for field in form %}
{% ifequal field.name "name" %}
<input id="id_name" type="hidden" name="name" value="{{ user.username }}" />
{% else %}{% ifequal field.name "email" %}
<input type="hidden" name="email" value="{{ user.email }}" id="id_email" />
{% else %}{{ field }}{% endifequal %}{% endifequal %}
{% endfor %}
<input type="submit" name="submit" value="Post">
</form>
{% endifequal %}