I am quite new to Django as such, and have been playing around with ModelForms. So far, I have been able to create ModelForms with ease. However, one problem seems to bug me a bit:
When a user fills the form, and if there is an error (say int instead of char or a missing blank=False value), the form spits out the error and seems to forget the values the user entered when the form failed to validate. I am wondering if there is a way to remember these values so that the user does not have to enter them again.
At the moment, I have something like the following:
class ContactForm(CreateView):
form_class = ContactForm
template_name = "superform.html"
success_url = '/ty/'
def form_valid(self, form):
# do something useful with validated form
return super(ContactForm, self).form_valid(form)
def form_invalid(self, form):
# do something useful with invalidated form.
return super(ContactForm,self).form_invalid(form)
I am assuming I need to do something in form_invalid to pass the values back to the form - but I am unsure how to do this(?).
I would really appreciate if someone could point me in the right direction.
Thanks.
----Edit---
<form class="form" role="form" action="{% url 'coolurl' %}" method="post">{% csrf_token %}
{% if form.non_field_errors %}
<div class="alert">
{% for error in form.non_field_errors %}
<span class="label">Error: {{ error|escape }}</span>
{% endfor %}
</div>
{% endif %}
<div class="form-group">
{% if form.name.errors %}
<div class="alert">
{% for error in form.name.errors %}
<span class="label">Error: {{ error|escape }}</span>
{% endfor %}
</div>
{% endif %}
<label class="col-sm-3" for="id_name">Name:</label>
<div class="col-sm-6">
<input class="form-control"
id="id_name"
type="text"
name="name"
maxlength="128"
placeholder="Your name..">
</div>
</div>
<button type="submit" class="btn btn-success">Submit</button>
</form>
Try to mention the value of the input tag:
<input class="form-control"
id="id_name"
type="text"
name="name"
maxlength="128"
value="{{ form.name.value|default_if_none:'' }}"
placeholder="Your name..">
</div>
You've taken the responsibility for rendering those fields away from Django by simply hard-coding them in the HTML. That means that Django has no way of inserting the current values; not only when redisplaying after errors, but also if you have a form that modifies existing database content.
Don't do it like that. I understand that you don't want to just juse form.as_p, but there is a good middle ground: output each field in the template with {{ form.my_field }}. You can add relevant classes to the fields in the definition in forms.py, and then Django will take care of outputting it correctly.
Related
How to make it such that if there are errors in the form, all the data I have keyed into the field remains and the error shows for me to edit what I need to edit.
Because it is very user-unfriendly if people press submit, and everything they have previously typed has to be retyped again due to an error that caused them to need to submit the form again.
just like when we post a stackoverflow question, if there are errors in our question eg time limit, whatever we have typed previously remains
Let me know if you require more code.
html
<form class="create-form" method="post" enctype="multipart/form-data">{% csrf_token %}
<div class="form-group">
<label for="id_title">Title</label>
<input class="form-control" type="text" name="title" id="id_title" placeholder="Title" required autofocus>
</div>
<button class="submit-button btn btn-lg btn-primary btn-block" type="submit">Submit</button>
</form>
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
views.py
def create_blog_view(request):
context = {}
user = request.user
if request.method == 'POST':
form = CreateBlogPostForm(request.POST or None, request.FILES or None)
if form.is_valid():
obj.save()
return redirect('HomeFeed:main')
else:
context['form'] = form
return render(request, "HomeFeed/create_blog.html", context)
You are rendering the fields manually by writing the tags. When you render using the form instance Django automatically sets the value attributes with the previous values. You can use {{ form.as_table }}, {{ form.as_p }}, {{ form.as_ul }}. You can also render fields individually using {{ form.title }} where title is the field name.
When I render the form in HTML, I use this view. the patient_id is used to denote what patient the check in is for and for name display and such.
def Checkin(request, patient_id):
patient = get_object_or_404(PatientInfo, pk=patient_id)
form = forms.PatientCheckinForm()
return render(request, 'patientRecords/checkin.html', {'patient': patient, 'form':form})
When I submit the patient form filled out as a POST method, I still need access to the patient_id. Currently this is the view that accepts the filled form:
def CheckinSubmit(request):
if request.method == 'POST':
form = forms.PatientCheckinForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save(commit=False)
instance.date_time_of_checkin = dt.now()
instance.patient = patient.patient_id
instance.save()
return redirect('patientRecords/index.html')
I want to set the instance.patient to the patient_id that was part of patient from the Checkin view. Is there a way to pass the patient data back along with the POST method or is there another way this can be done?
For reference, here is my template and I am using ModelForm not form.
{% block content %}
<div class="container">
<h1>Patient Checkin</h1>
<h2>{{patient.first_name}} {{patient.last_name}}</h2>
</div>
<div class="container">
<form action="{% url 'patientRecords:checkinsubmit' %}" method="POST" class="form">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Submit</button>
{% endbuttons %}
</form>
</div>
{% endblock %}
Thanks in advance!
You should be able to simply add a hidden input to your form to capture the patient ID:
{% block content %}
<div class="container">
<h1>Patient Checkin</h1>
<h2>{{patient.first_name}} {{patient.last_name}}</h2>
</div>
<div class="container">
<form action="{% url 'patientRecords:checkinsubmit' %}" method="POST" class="form">
<input type="hidden" name="patient_id" value="{{patient.patient_id}}" />
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Submit</button>
{% endbuttons %}
</form>
</div>
{% endblock %}
(Note this assumes that the patient ID is accessible from the patient_id property of the patient object.)
Then, in your CheckinSubmit method, you can access this value via request.POST.get('patient_id')
Alternatively, it appears that your check in form loads with the patient ID in the URL. In your CheckinSubmit method, you should be able to access this URL through the request.META.HTTP_REFERER property. You could then parse the URL (e.g., using request.META.HTTP_REFERER.split('/')[len(request.META.HTTP_REFERER.split('/')) - 1] to pull out the patient ID.
Example
<form method="post" action = "{% url 'user_search_from_group' %}">
<div class="module-option clearfix">
<div class="input-append pull-left">
<input type="hidden" name="groupname" value="{{ gpname }}" />
{% csrf_token %}
<input type="text" class="span3" placeholder="Filter by name" id="username3" name="username3" required>
<button type="submit" class="btn" name="submit">
<i class="icon-search"></i>
</button>
</div>
</div>
</form>
Here a hidden field is used to pass a value along form.
def user_search_from_group(request):
if request.method == 'POST':
username3 = request.POST.get('username3')
gname = request.POST.get('groupname')
Using request we are use the value inside view
I am using 'django-multiselectfield' in my model form so users can select multiple 'areas' in my form. What I want to know now is how can I style the multiselectfield using css.
I have tried simply adding a class to the form field in my template like I normally do with all the other types of model fields, but no luck:
{% render_field form.area class="area" %}
Any help would be much appreciated.
This is described in `django-multiselectfield' documentation. Specifically,
It is possible to customize the HTML of this widget in your form template. To do so, you will need to loop through form {field}.field.choices. Here is an example that displays the field label underneath/after the checkbox for a MultiSelectField called providers:
Here is a example of what i did.
{% for field in form %}
<div class="fieldWrapper">{{ field.errors }}
<input type="{{ field.field.widget.input_type }}" name="{{ field.html_name }}" id="{{field.id_for_label}}" class="col-12 input-login" placeholder="{{field.label}}" >
{% if field.help_text %}
<p class="help">{{ field.help_text|safe }}</p>
{% endif %}
</div>
{% endfor %}
Now you can customise the field as if it is a simple html input field.
This page has a wonderful detailed explanation. Kudos to Vitor
------edit-----
Almost forgot. This is the example for normal forms. You have to go through the official docs of django-multiselectfield to get field attribute names, and replace the respective attributes
I suggest you render the form manually and loop through it.
Say you have a Choice model with a foreignkey to a Question model, you could do it like this:
<form class="qform" action="" method="post">
{% csrf_token %}
<p class="title">{{ form.question }}</p>
{% for choice in form.choice_set.all %}
<input type="checkbox" name="{{ choice }}" id="{{ choice }}{{ forloop.counter }}"
value="{{ choice.id }}">
<label for="{{ choice }}{{ forloop.counter }}">{{ choice }}</label>
{% endfor %}
</form>
You can reference the input and label in your style.css like:
input[type="checkbox"]{
some: stuffs...;
}
label {
some: stuffs...;
}
I hope this helps.
I have a list of multiple objects from my database (named "plp's"), arranged in a table. Next to each "plp" element I have a button "Edit" to modify that particular entry.
Next, I redirect the user to a new url, where I pass the id of that "plp", and show the form to edit it, with a "save" button.
After pressing the "save", which is request.POST, I want to redirect the user back to the first url, with the list of all the "plp" objects in one list. That means to the site, where he first pressed "Edit".
Can I somehow save the url of where the "Edit" was clicked, and pass it to my views.py?
Thank you
listdns.html:
<td>
Uredi
</td>
urls.py:
rl(r'^(?P<plp_id>\d+)/uredi$', plp_list_uredi,name="plpuredi")
views.py:
def plp_list_uredi(request, plp_id=None):
moj_plp=PLPPostavka.objects.get(id=plp_id)
form=PLPPostavkaForm(request.POST or None,request=request,dns=moj_plp.dns, instance=moj_plp)
context ={
'plp':moj_plp,
'form':form,
}
if request.POST:
if form.is_valid():
plp = form.save()
return redirect(request.path)
return render(request, "plp_pos/uredi.html",context)
uredi.html
<form action="" method="POST">
{% csrf_token %}
<div class="box">
<div class="box-header">
<h4 class="box-title">
Urejanje PLP Postavke
</h4>
</div>
<div class="box-body">
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}" class="col-md-2 control-label detail">{{ field.label }}</label>
<div class="col-md-10">
{% if field|field_type == "datefield" %}
{% render_field field class+="form-control dateinput" %}
{% else %}
{% render_field field class+="form-control" %}
{% endif %}
</div>
</div>
{% endfor %}
</div>
<div class="box-footer">
<div class="box-tools pull-right">
<input type="submit" value="Shrani" class="btn btn-primary" />
</div>
</div>
Don't you only have 1 page to edit all the elements? Then you could perhaps hardcode the link e.g.
return HttpResponseRedirect(my_edit_url)
If this doesn't work and you need to go 2 pages back take a look at this post:
How to redirect to previous page in Django after POST request
I've been racking my brain over this problem for the past few days and I've read numerous other questions regarding the same error but they all seem to be different cases (not including management form, forgetting to update TOTAL_FORMS, etc etc) and do not resolve my problem. I have a page which could contain multiple formsets in a single HTML form. When I am posting the data back to the server, it fails on the is_valid() check for the formsets with the error in the title. I am new to web development and Django so please forgive me if I made a silly mistake or am taking an approach that will not work.
def purchase(request):
return generic_form_view(request, "inventory_tracking/add_purchases.html",
"Successfully added purchases for %s.",
PurchaseForm,
[formset_factory(PurchaseForm.LiquorForm),
formset_factory(PurchaseForm.NonLiquorForm)])
def generic_form_view(request, template, success_message, ParentForm, FormSets):
if request.method == 'POST':
request_params = copy(request.POST)
parent_form = ParentForm(request_params)
formsets = list(map(lambda form_set: form_set(request_params), FormSets))
if parent_form.is_valid(): # This works.
for formset in formsets:
if formset.is_valid(): # Fails here.
Here is a snippet from my template:
<form action="{% block form_action %}{% endblock %}" method="post">
{% csrf_token %}
<div class="row">
<div class="row">
<div class=" well well-lg">
<div class="row">
{{ parent_form.management_form }}
{% for field in parent_form %}
<div class="col-lg-6">
<div class="form-group">
<label class="control-label">{{ field.label }}</label>
{{ field }}
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
<div class="row">
{% for formset in formsets %}
{{ formset.management_form }}
<div class="row">
<div class="well well-lg">
{% for form in formset %}
<div id="{{ form.prefix }}" class="row">
...
I've been trying to debug this and I noticed something a little interesting but since I am not too familiar with Django it could be a red herring. In the POST, I see the management_form data for the formsets I am creating but I do not see the management_form data for the parent formset (in this case PurchaseForm). However the parent_form is passing validation and the other formsets are not.
I expected this to be a silly problem and I turned about to be right! When my generic_form_view method creates the formsets on the GET request I was adding a prefix like the documentation mentioned but I was not adding a prefix when creating the formsets on the POST.