I am working with inlineformset_factory and created a forms.BaseInlineFormSet to override the clean function to make some validations. the validation works and the form isn't accepted but the ValidationError message doesn't appear when the page is reloaded.
here is my form class:
class BaseDetailFormSet(forms.BaseInlineFormSet):
def clean(self):
super(BaseDetailFormSet, self).clean()
if any(self.errors):
return self.errors
for form in self.forms:
product = form.cleaned_data['product']
if form.cleaned_data['quantity_sold'] > product.quantity_in_stock:
raise forms.ValidationError(_('not enough products'))
DetailFormset = inlineformset_factory(Invoices,
InvoiceDetail,
fields=('product', 'quantity_sold'),
widgets= {'product': forms.Select(
attrs={
'class': 'search',
'data-live-search': 'true'
})},
formset=BaseDetailFormSet,
extra=1)
and my html code:
<form class="form-inline" method="post" action="">
{% csrf_token%}
{% include '_layouts/form_snippet.html' %}
<table class="table">
{{inlines.management_form}}
{%for form in inlines.forms%}
{% if forloop.first%}
<thead>
<tr>
{%for field in form.visible_fields%}
<th>{{field.label|capfirst}}</th>
{%endfor%}
{%endif%}
</tr>
</thead>
<tr class="formset_row">
{{ form.non_form_errors }}
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<button type="submit" value="Add" class="btn btn-inverse">{% trans 'Submit Invoice' %}</button>
</form>
any ideas how to show the validation error message or why it isn't shown.
non_form_errors belongs to the formset not the form.
So you can display it with:
{{ formset.non_form_errors }}
Or in your case, since you use the variable inlines:
{{ inlines.non_form_errors }}
Since it belongs to the formset, you should do this outside of the forloop through the forms.
If you have errors on the form that don't belong to any field, then you access these with
{{ form.non_field_errors }}
Related
I have created a Flask WTForm. I used the form for Add/Edit/Delete purpose. I want dynamically change label/value of the SubmitField according to the purpose
Form Class
class UserForm(Form):
username = StringField('User Name',validators=[validators.required(), validators.Length(max=32)])
password = PasswordField('Password',validators=[validators.required(), validators.Length(min=8, max=16)])
confirm_password = PasswordField('Confirm Password',validators=[validators.required(), validators.EqualTo('password', message='Both password fields must be equal !')])
name = StringField('Name',validators=[validators.required(), validators.Length(max=32)])
submit = SubmitField('Submit')
HTML Template
<title>User Form</title>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<ul>
{% for message in messages %}
<li>{{ message[1] }}</li>
{% endfor %}</ul>
{% endif %}
{% endwith %}
<table height="100%" width="100%" >
<tr><td align="center" valign="middle">
<form method="post" action="/process/{{ option }}">
<table>
{{form.csrf }}
<tr><td>{{ form.username.label }}</td></tr>
<tr><td>
{% if option == "new" %}
{{ form.username() }}
{% else %}
{{ form.username(readonly=true) }}
{% endif %}
</td></tr>
<tr><td>{{ form.password.label }}</td></tr>
<tr><td>{{ form.password }}</td></tr>
<tr><td>{{ form.confirm_password.label }}</td></tr>
<tr><td>{{ form.confirm_password }}</td></tr>
<tr><td>{{ form.name.label }}</td></tr>
<tr><td>{{ form.name }}</td></tr>
{% if option == "new" %}
{{ form.submit() }} **submit label with 'New'
{% else %}
{{ form.submit() }} **submit label with 'Edit'
{% endif %}
</table>
</form>
</td>
</tr>
</table>
Update the label at runtime in your view:
For example:
from wtforms import Label
def some_view():
_form = UserForm()
_form.submit.label = Label(_form.submit.id, 'New' if option == 'new' else 'Edit')
return render_template('index.html', form=_form)
Edit November 2022
It's even easier to simply modify the label's instance text property.
def some_view():
_form = UserForm()
_form.submit.label.text = 'New' if option == 'new' else 'Edit'
return render_template('index.html', form=_form)
I just want to put the username of the logged in user inside the author field and at the same time assign the author field as the user's username in the database.
def create_vacation(request):
form = creationForm(request.POST or None)
vacations = Vacation.objects.all()
vacations.author=request.user.username
if form.is_valid():
form.save()
return redirect('/vacations/all')
return render(request, 'creationForm.html', {'form': form})
and this is the vacation model
class Vacation(models.Model):
vacationInformation = models.TextField()
startDate= models.DateTimeField('start date')
endDate = models.DateTimeField('end date')
author = models.ForeignKey(User, null=True, on_delete=models.SET_NULL)
def __str__(self):
return self. vacationInformation
this is the form template
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger" role="alert">
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
{% endif %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% if form.is_bound %}
{% if field.errors %}
{% render_field field class="form-control is-invalid" %}
{% for error in field.errors %}
<div class="invalid-feedback">
{{ error }}
</div>
{% endfor %}
{% else %}
{% render_field field class="form-control is-valid" %}
{% endif %}
{% else %}
{% render_field field class="form-control" %}
{% endif %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
{% if vacation %}
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">Submit</button>
</form>
I can render all of the vacation data in the homepage normally except for sure the author name
{% for vacation in vacations %}
<tr>
<td>{{ vacation.vacationInformation }}</td>
<td>{{ vacation.startDate }}</td>
<td>{{ vacation.endDate }}</td>
<td>{{ vacation.author }}</td>
It is giving me the entire user names in the author field in the form but I just want the user whom is logged in.
You need to get the currently logged in User. Then you can use the User to fill in the initial values in the form.
from django.contrib.auth import get_user
# ...
def create_vacation(request):
if request.method == 'POST':
form = creationForm(request.POST)
if form.is_valid():
form.save()
return redirect('/vacations/all')
else:
# Get the currently logged-in User.
user = get_user(request)
# Provide User as initial data to the form
form = creationForm(initial={'author': user})
return render(request, 'creationForm.html', {'form': form})
Relevant documentation:
ModelForm - Providing initial values
django.contrib.auth.get_user
I am using django-filter to search a model. Here is the code:
filters.py:
class PersonFilter(django_filters.FilterSet):
lastName = django_filters.CharFilter(lookup_expr='icontains')
firstName = django_filters.CharFilter(lookup_expr='icontains')
class Meta:
model = Person
fields = ['lastName', 'firstName']
views.py:
def search(request):
people = Person.objects.all()
people = PersonFilter(request.GET, queryset=people)
context = {'filter': people}
return render(request, 'myapp/template.html', context)
template.html:
<form method="get">
{{ filter.form.as_p }}
<button type="submit">Search</button>
</form>
<table>
{% for field in filter.qs %}
<tr>
<td>
{{ field.idno }}
</td>
<td>
{{ field.lastName }}
</td>
<td>
{{ field.firstName }}
</td>
<td>
{{ field.status }}
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
</body>
</html>
Right now, this results in a table that mirrors my model with search boxes for first name and last name. The search works perfectly.
How do I prevent the table of data from showing up initially? Logically, this could be done superficially (hide) or, better yet, substantially (dynamically create queryset). Is this possible?
You can leverage the FilterSet's is_bound property, although you would need to change the view code to only provide the request query params when the form has been submitted.
def search(request):
submitted = 'submitted' in request.GET
data = request.GET if submitted else None
people = PersonFilter(data, queryset=Person.objects.all())
return render(request, 'myapp/template.html', {'filter': people})
<form method="get">
{{ filter.form.as_p }}
<button type="submit" name="submitted">Search</button>
<!-- ^^^^ added 'name' parameter -->
</form>
{% if filter.is_bound %}
<table>
{% for person in filter.qs %}
...
If I have a django form, where I want it to provide a bootstrap error input (<input type="text" id="inputError">) to show an error instead of the bullet-list {{ form.title.error }}, how would I go about doing this? Right now I have:
{% if UF.title.errors %}
{{ UF.title }}
{{ UF.title.errors.as_text }}
{% else %}
{{ UF.title }}
{% endif %}
but instead of having UF.title in the first error, I would rather have UF.title but with the ERROR> how would I go about doing this?
You should use django-bootstrap3.
It does exactly what you want.
{% load bootstrap3 %}
{# Display a form #}
<form action="/url/to/submit/" method="post" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">
{% bootstrap_icon "star" %} Submit
</button>
{% endbuttons %}
</form>
Here's what I do for my code:
<div class="form-group {% if form.username.errors %}has-error{% endif %}">
{{ form.username }}
{% if form.username.errors %}
<span class="help-block">{{ form.username.errors.0 }}</span>
{% endif %}
</div>
And my forms.py looks like:
from django import forms
class LoginForm(forms.Form):
username = forms.CharField(label='Username', max_length=32, widget=forms.TextInput(attrs={'placeholder': 'Username', 'class': 'form-control'}))
I am new to django , please bear with me ..
created a template using django formsets and it works all good except i am not able to give header to the can_delete column .
My Django views.py looks like :
def add_expenditure(request):
context = RequestContext(request)
ExpFormSet = modelformset_factory(Expenditure,extra=1,max_num=10,fields=('exp_date', 'description','amount'),can_delete=True)
if request.method == 'POST':
formset = ExpFormSet(request.POST)
if formset.is_valid():
formset.save(commit=True)
formset = ExpFormSet()
else:
print "errors in formset are ",formset.errors
else:
formset = ExpFormSet(queryset=Expenditure.objects.none())
return render_to_response('moni/add_expenditure.html', {'formset':formset}, context)
templete form code is as below :
<form id="expenditure_form" method="post" action="/moni/add_expenditure/">
{% csrf_token %}
<table border=10>
<tr>
<th>Serial No.</th>
<th><label >Date:</label></th>
<th><label for="id_description">Description:</label></th>
<th><label for="id_amount">Amount</label></th>
</tr>
{{ formset.management_form }}
{% for form in formset %}
<tr>
<td>{{forloop.counter}}</td>
{% for field in form %}
<td> {{ field.class }} {{ field }}
{% if field.name == "exp_date" %}
<a href="javascript:void(0)" class="todaylink" id= {{forloop.parentloop.counter0}} >Today</a>
{% endif %}
</td>
{% endfor %}
</tr>
<br>
{% endfor %}
</table>
<input type="submit" name="submit" value="Create Expenditure" />
</form>
and template looks like :
What i want is to have a header for delete check box as well .. just next to amount . I tried adding one more header but seems its not working as intended . Can someone provide any advices .. Below is how it looks after adding one more header
<form id="expenditure_form" method="post" action="/moni/add_expenditure/">
{% csrf_token %}
<table border=10>
<tr>
<th>Serial No.</th>
<th><label >Date:</label></th>
<th><label for="id_description">Description:</label></th>
<th><label for="id_amount">Amount</label></th>
<th><label for="id_delete">Delete</label></th>
</tr>
{{ formset.management_form }}
{% for form in formset %}
<tr>
<td>{{forloop.counter}}</td>
{% for field in form %}
<td> {{ field.class }} {{ field }}
{% if field.name == "exp_date" %}
<a href="javascript:void(0)" class="todaylink" id= {{forloop.parentloop.counter0}} >Today</a>
{% endif %}
</td>
{% endfor %}
</tr>
<br>
{% endfor %}
</table>
<input type="submit" name="submit" value="Create Expenditure" />
</form>
Any Advice as to how to create this header above delete check box ?
The problem is
{% for field in form %}
<td> {{ field.class }} {{ field }}
{% if field.name == "exp_date" %}
<a href="javascript:void(0)" class="todaylink" id= {{forloop.parentloop.counter0}} >Today</a>
{% endif %}
</td>
{% endfor %}
This create 5 TD and you have 4 TD in header.
Please try this
<tr>
<th>Serial No.</th>
<th><label >Date:</label></th>
<th><label for="id_description">Description:</label></th>
<th><label for="id_amount">Amount</label></th>
<th></th>
<th><label for="id_delete">Delete</label></th>
</tr>
Also you can remove element before delete checkbox.