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)
Related
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 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 }}
In models.py,I created a character type field called "category".After the user enters the category name,it is saved in the database and now I want to display all the category names stored in the database.I created four category names.I can see all four in the database but when displaying it in the UI, I see NONE instead of the category names.
views.py,
def add_cat(request):
form = CatForm(request.POST or None)
context = {"form":form}
if form.is_valid():
instance = form.save(commit=False)
category = form.cleaned_data.get("category")
instance.category = category
instance.save()
messages.add_message(request, messages.INFO, 'Category Added')
return render(request,"add-cat.html",context)
forms.py,
class CatForm(forms.ModelForm):
class Meta:
model = Add_cat
fields = ['category']
My template file,
{% extends "admin-menu.html" %}
{% block content %}
{% load staticfiles %}
<head>
<link rel="stylesheet" href="{% static 'style.css' %}">
</head>
<h2 style="text-align: center;">Add Category</h2>
<form method="POST">
{% csrf_token %}
<table align="center">
{{form.as_table}}
</table>
<input type="submit" value="Add" style="margin-left: 48%;"/>
<input type="reset" value="Cancel"/>
</form>
{% if messages %}
<ul class="messages" style="list-style-type: none;">
{% for message in messages %}
<li{% if message.tags %} class="{{ message.tags }}"{% endif %}>{{ message }}</li>
{% endfor %}
</ul>
{% endif %}
 
<form id="id1">
{% for field in form %}
<table align="center">
<tr><th>Category Name</th></tr>
<tr><td>{{field.value}}</td></tr>
</table>
{% endfor %}
</form>
{% endblock %}
Try like this
def add_cat(request):
form = CatForm(request.POST or None)
catagories = <model>.objects.all()
context = {"form":form, 'categories':categories}
if form.is_valid():
instance = form.save(commit=False)
category = form.cleaned_data.get("category")
instance.category = category
instance.save()
messages.add_message(request, messages.INFO, 'Category Added')
return render(request,"add-cat.html",context)
In templates
{% for category in categories %}
<table align="center">
<tr><th>Category Name</th></tr>
<tr><td>{{ category }}</td></tr>
</table>
{% endfor %}
I'm having an issue with Django while updating inline formsets. I want to update a form and an inline formset, but it raises this error:
"MultiValueDictKeyError"
Exception Value: "u'clientphone_set-0-id'"
This is my view:
def edit_client(request, client_id):
client_to_edit = Client.objects.get(id=client_id)
form = ClientForm(instance=client_to_edit)
phone_formset = ClientPhoneFormSet(instance=client_to_edit)
if request.method == 'POST':
form = ClientForm(request.POST, instance=client_to_edit)
if form.is_valid():
form_saved = form.save(commit=False)
phone_formset = ClientPhoneFormSet(request.POST, request.FILES, instance=form_saved)
if phone_formset.is_valid():
form_saved.save()
phone_formset.save()
return client(request)
return render(request, 'WorkOrders/add_client.html', {'form' : form, 'phone_formset' : phone_formset})
And these are my forms
class ClientForm(forms.ModelForm):
name = forms.CharField(max_length=128)
rif = forms.CharField(max_length=10)
address = forms.CharField(max_length=250)
class Meta:
model = Client
fields = ('name','rif','address',)
ClientPhoneFormSet = inlineformset_factory(Client, ClientPhone, extra=1, fields=('phone_number',), can_delete=True)
And this is my template
<form class="form-horizontal" id="add_client" method="post" action=".">
{% csrf_token %}
{% for field in form.visible_fields %}
<div class="form-group">
<label for="{{ field.id }}" class="col-sm-2 control-label">{{ field.help_text }}</label>
<div class="col-sm-10">
{{ field.errors }}
{{ field }}
</div>
</div>
{% endfor %}
<div class="form-group">
<label for="{{ field.id }}" class="col-sm-2 control-label">Teléfono</label>
<div class="col-sm-10">
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
{% for form in phone_formset.forms %}
<tr>
<td>
{% if form.instance.pk %}
{{ form.DELETE }}
{% endif %}
{{ form.errors }}
{{ form.phone_number }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
{{ phone_formset.management_form }}
<div class="form-group">
<div class="col-sm-offset-2 col-sm-10">
<button type="submit" class="btn btn-primary">Guardar</button>
Volver
</div>
</div>
</div>
</form>
It opens the form with no problem, the error happens when I submit.
Thanks.
I fixed it by using phone_formset instead of phone_formset.forms, and added the {{ phone_field.id }}.
Here is the result:
<table border="0" cellpadding="0" cellspacing="0" width="100%">
<tbody>
{% for phone_field in phone_formset %}
<tr>
<td>
{% if phone_field.instance.pk %}
{{ phone_field.DELETE }}
{% endif %}
{{ phone_field.errors }}
{{ phone_field.id }}
{{ phone_field.phone_number }}
</td>
</tr>
{% endfor %}
</tbody>
</table>
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.