Django: Crispy Forms Validation Error in Template - django

I'm using django-crispy-forms for nicely rendered forms.
For one of my forms, I had to make some custom adjustments and am now wondering how to properly validation errors that don't belong to a specific field but to the whole form.
Specifically, my form contains a start and end date:
# forms.py
class PlotForm(forms.Form):
start_date = forms.DateField(initial=last_month, widget=forms.widgets.DateInput(attrs={'type': 'date'}))
end_date = forms.DateField(initial=datetime.date.today, widget=forms.widgets.DateInput(attrs={'type': 'date'}))
def clean(self):
cleaned_data = super().clean()
start_date = cleaned_data.get('start_date')
end_date = cleaned_data.get('end_date')
if start_date > end_date:
raise forms.ValidationError("Start date must not be before end date.")
return start_date
To check that the end date is not before the start date, I use clean(self).
However, the error is never displayed.
<form method="post">
{% csrf_token %}
<div class="row">
<div class="col-6">
{{ form.start_date|as_crispy_field }}
</div>
<div class="col-6">
{{ form.end_date|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I can fix this by adding {{ form.errors }} but that looks really ugly. Is there any simple way to nicely render form-related validation errors with crispy?
The field-related errors are shown nicely.

You can use the |as_crispy_errors template filters [readthedocs]:
<form method="post">
{% csrf_token %}
{{ form|as_crispy_errors }}
<div class="row">
<div class="col-6">
{{ form.start_date|as_crispy_field }}
</div>
<div class="col-6">
{{ form.end_date|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
</form>
This template filter will:
Renders only form errors the same way as django-crispy-forms.

https://stackoverflow.com/a/73251040/12199738
Simplest solution was just adding the novalidate attribute to my form.

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

Multiple formsets only saves the number of formsets in 'extra' attribute: Why?

Short version: Creating dynamically multiple formsets only saves as much formsets specified in extra attribute instead of as many as I've.
Long version: I'm doing my first project with Django and I've to create a CreateView with a Parent and a Child forms. I need to create as many nested Child forms the user wants, so I've created a small code in Javascript that adds/removes the child forms. This is working fine.
The problem is: I can only save as many forms as I've specified in the extra attribute. If I say '3', it creates 3 Child forms and I can save 3 of them, even if I create/remove Child forms. If I say '1', I can create 10, but only will save 1. Seems like I'm missing something in the template, but I don't know what. Here's the relevant code:
forms.py
class ParentForm(ModelForm):
class Meta:
model = Parent
exclude = ()
class ChildForm(ModelForm):
class Meta:
model = Child
fields = ....
ChildFormSet = inlineformset_factory(Parent, Child, form=ChildForm, can_delete=True, extra=1)
views.py
class ParentCreateView(LoginRequiredMixin, CreateView):
model = Entrada
fields = ...
def get_context_data(self, **kwargs):
data = super(ParentCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['child_form'] = ChildFormSet(self.request.POST)
data['materials'] = Material.objects.all()
else:
data['child_form'] = ChildFormSet()
data['materials'] = Material.objects.all()
return data
def form_valid(self, form):
context = self.get_context_data()
child_form = context['child_form']
with transaction.atomic():
self.object = form.save()
if child_form.is_valid():
child_form.instance = self.object
child_form.field1 = self.object.id
child_form.save()
return super(ParentCreateView, self).form_valid(form)
template.html
<form class="own-form" action="" method="post">
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
<h2 class="text-center text-header"> New Entry</h2>
<div class="form-group">
{% for field in form.visible_fields %}
<div class="form-group row">
<div class="col-4 text-center">{{ field.label_tag }}</div>
<div class="col-8">{{ field }}</div>
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
</div>
<hr>
<div class="form-group form-material-box row form-0">
<div class="col-3 text-center">
<label>Total weight: </label>
<input type="number" id="kg_0">
</div>
<div class="col-3 text-center">
<label>Boxes to create: </label>
<input type="number" id="num_boxes_0">
</div>
<div class="col-3 text-center">
<label>Material: </label>
<br>
<select name="item_id" id="material_0">
{% for material in materials %}
<option value="{{ forloop.counter }}">{{ material }}</option>
{% endfor %}
</select>
</div>
<div class="col-3 text-center">
<button type="button" id="create_boxes_0" class="btn btn-danger">Create</button>
</div>
<!-- Nested forms with desired number of boxes -->
<div id="nested_forms_0">
<div class="row" id="box_0">
{% for bala in bala_form %}
<div class="col-3 text-center">
<h5>Bala #1: </h4>
</div>
<div class="col-2 text-center">
{{ bala.kg }}
</div>
<div class="col-2 text-center" >
{{ bala.material }}
</div>
<div class="col-2 text-center" >
{{ bala.box_price }}
</div>
<div class="col-3 text-center">
<button type="button" id='remove_box_0' class="btn btn-danger">Remove box</button>
</div>
{% endfor %}
</div>
</div>
</div>
<p>
{{ child_form.management_form }}
<input id="create" class="btn btn-secondary btn-lg btn-block btn-option btn-form" type="submit" value="Crear" />
</p>
Edit:
javascript relevant code
// Adds the difference between desired boxes and current boxes
function add_boxes(form_id, total, num){
for (let i = total; i != total+num; i++) {
var element = $('#nested_forms_' + form_id + "> :first-child ").clone().css('display', 'none');
element.appendTo('#nested_forms_' + form_id).show('250');
};
$('#id_child-TOTAL_FORMS').val(num + total);
};
// Removes every unneeded boxes
function remove_boxes(form_id, total){
$('#nested_forms_' + form_id).children().each(function(){
if ($(this).index() >= total) {
$(this).fadeTo(150, 0).slideUp(150, function(){
$(this).remove();
});
}
});
$('#id_child-TOTAL_FORMS').val(total);
}
What I'm missing?
Update
I've printed the information passed to Django from the template. Seems like its just getting the empty form without any value when I'm creating more than extra forms. Comparing the diffs when I'm creating 3 forms dynamically (left) and hardcoding 3 as the extra value (right) shows it (bales-materies-primeres is the name of the Child):
https://www.diffchecker.com/XskfB9y3
Your Javascript needs to modify the value of the form-TOTAL_FORMS field in the management form. See the formsets documentation.

ValueError when using ChoiceField

I want to create a Django form that allows users to select options from a dropdown menu, but I am getting a value error:
ValueError: need more than 1 value to unpack
Form:
class DropdownForm(forms.Form):
def __init__(self,*args,**kwargs):
choices = kwargs.pop('choices')
label = kwargs.pop('label')
super(DropdownForm,self).__init__(*args,**kwargs)
self.fields['selected'].label = mark_safe(label)
self.fields['selected'].choices = choices
selected = forms.ChoiceField(widget=forms.Select(attrs={'class':"form-control text-center"}))
View.py:
form_rate = DropdownForm(choices=[("HIGH","HIGH")],label="RATE",prefix="Rate")
form_pass_setup = DropdownForm(choices=[("AUTO","AUTO"),("MANUAL","MANUAL")],label="Pass Setup",prefix="pass_setup")
form_dict.update({'form_rate':form_rate,'form_pass_setup':form_pass_setup})
return render(request,'Nominal.html',form_dict)
Template:
<form action="/InterfaceApp/Nominal_Request/" method="post" class="form">
{% csrf_token %}
<div class="panel-body text-center">
<div class="row pad_forms">
<div class="col-xs-3">
{% bootstrap_form form_rate %}
</div>
<div class="col-xs-3">
{% bootstrap_form form_pass_setup %}
</div>
</div>
<br><br>
<button type="submit" class="btn btn-primary center-block" value="Submit" name="Single">
{% bootstrap_icon "fire" %} Generate a Single Requests
</button>
</div>
</form>
Can anyone tell me why I'm getting this value error?
The problem might occur cause you have not set the required argument choices for your ChoiceField.
Here is a link to the docs: https://docs.djangoproject.com/en/1.8/ref/forms/fields/#choicefield.

django form is invalid but has no errors

my datefields in my django form render always as invalid, but no errors of the how and why are given. (I use both non_field_errors as field.errors )
My Form
class FilterJournalForm(forms.Form):
end_initial = datetime.now(utc)
from_initial = (end_initial - timedelta(days=30))
from_date = forms.DateField(
widget=forms.DateInput(format='%m-%d-%Y'),
initial=from_initial,
required=True,
)
end_date = forms.DateField(
widget=forms.DateInput(format='%m-%d-%Y'),
initial=end_initial,
required=True,
)
part of my view that has the form:
filter_form = FilterJournalForm(request.POST or None)
if request.POST:
print request.POST
if filter_form.is_valid():
print "success"
My template part:
<form class="form-inline" action="" method="POST">
{% csrf_token %}
<div class="form-group">
{{ filter_form.from_date|add_css_class:"form-control input-sm" }}
</div> until
<div class="form-group">
{{ filter_form.end_date|add_css_class:"form-control input-sm" }}
</div>
<button type="submit" class="btn btn-primary btn-sm" >Filter</button>
{% if filter_form.errors %}
<div id="form-error">
<p>The operation could not be performed because one or more error(s) occurred.<br />{{ filter_form.non_field_errors }}</p>
<ul>
{% for field in form %}
<li>{{ field.errors|striptags }}</li>
{% endfor %}
</ul>
</div>
Any idea what is going wrong here? (i also tried to change the initial input of my formfields to date.today() to see if datetime objects could be ruining it. But that as well is not the problem.
{% endif %}