I am having a difficult time understanding the Django docs on this one. I have also come across some other threads with the same question, but I cannot seem to get the suggested answers to work for me. I think it is because I am posting text, and it is not considered "clean" data?
I want to autofill two form fields, then when the user hits the submit button, it saves. But for some reason only the boolean value is working, not the text value. Any ideas?
You will also see the fields are hidden from my template. When I show these fields, the initial values are set correctly as I expected, but when I hit submit, only the boolean is saved to database correctly.
EDIT
It works fine when I don't hide the form fields using {{ form }} in my template.
It does not work when I hide the fields using {{ form.field.as_hidden }}
Boolean field is accepted, but the text field is not.
I am trying to autofill this field with a text value, hide it, and submit this value when the submit button is pressed...
views.py
class BuildStopView(LoginRequiredMixin,UpdateView):
model = Build
form_class = StopBuild
template_name = 'build_stop.html'
login_url = 'login'
forms.py
class StopBuild(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(StopBuild, self).__init__(*args, **kwargs)
self.initial['buildEndType'] = 'manuallyStopped'
self.initial['buildActive'] = False
class Meta:
model = Build
fields = ['buildEndType','buildActive']
(template) stop_build.html
{% extends 'base.html' %}
{% block body %}
<style>
div.a {
text-align: center;
}
</style>
<div class = "a">
<h3>Are you sure you want to stop this build manually?</h3>
</div>
<form action="" method="post">{% csrf_token %}
{{ form.field.as_hidden }}
<button class="btn btn-danger ml-2" type="submit">Stop Build Manually</button>
</form>
{% endblock %}
form.field.as_hidden does not output the fields as hidden, in fact it does not do anything at all because you don't have a field called field in your form. You need to refer to the actual fields:
{{ form.buildEndType.as_hidden }}
{{ form.buildActive.as_hidden }}
However, if you want these to always be shown as hidden, you should probably do it in your form definition, by declaring them with HiddenInput widgets.
Related
I have a form:
class InspectionReportForm(forms.ModelForm):
class Meta:
model = InspectionReport
fields = ('Date', 'Comment', 'Signature')
labels = {"Date": "Date (YYYY-MM-DD)"}
def __init__(self, *args, **kwargs):
super(InspectionReportForm, self).__init__(*args, **kwargs)
self.fields["Comment"].widget = forms.HiddenInput()
self.fields["Date"].widget = forms.HiddenInput()
self.fields["Signature"].widget = forms.HiddenInput()
Which stores a report, in addition to the date and who wrote it.
In the case of a "Pass" or "Fail" report. I want a generic comment in the report, so this would be auto-filled, which is why I have hidden this field. Date and Signature will be taken from the date of submission and the signature will be the logged in user. So mostly all auto-filled.
However I have a "Maybe" option which prompts the user for a comment and submits that instead of the generic ones.
My question is how do I change the comment value of the form from the HTML Template/Javascript stage of my project?
My current code is as follows:
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<center><button type="button" id="pass">Pass</button>
<button type="button" id="fail">Fail</button>
<button type="button" id="check" class="collapsible">Check</button>
<div class="content"> Comment: <input type="text" name ="comments">
<button id="submitCheck">Submit</button>
</div>
</center>
The Check button is just a collapsible container that has an input box and a submit button for the "Maybe" option.
As far as Javascript I have nothing really because I can't directly change the field value from here. I've tried selecting the field with {%for field in form%} but had no luck
You could do the "dirty trick" of copying the contents of your comment field into the hidden Comment field using javascript:
$("input[name=comments]").on("change", function(){
$("input[name=Comment]").val($(this).val());
})
Or, what I believe is a better approach, don't use {{ form.as_p }} but render the fields separately so that you can render the {{ form.Comment }} field directly inside your collapsible. Then you don't have to do anything in javascript. You can render a field's label, the field itself and the field errors like this:
{{ form.Date.label_tag }}
{{ form.Date }}
{{ form.Date.errors }}
How can I access if a field has)errors at the level of widget?
Using default I tried:
{% if widget.attributes.has_errors %} or {% if widget.has_errors %}
but are not working.
I use custom widget templates, I'm thinking to use a custom form Field and overwrite the default field.
I know clean method exist but I don't know how to push to the widget the dynamic(non default) data/attributes I want.
I tried:
class AWidget(forms.Widget):
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
has_errors = context['widget']['attrs'].pop('has_errors', None)
context['widget']['has_errors'] = has_errors
It works for errors but I don't know if is the best option plus I want to pass other values/attributes from Form Field,and I think will be better to try to overwrite the Form Field but I don't know exactly how.
Also accessing individual attributes using:
{{ widget.attrs.maxlength }} or {{ widget.attrs.items.maxlength }}
even if accedes in a for loop works
I know I can add a parent div with a class of error:
<div class="{% if form.field.errors %}pass_error{% endif %}">
{{ form.field }}
</div>
but, that implies big changes at the css level.
I already overwrite all Django widgets with custom widgets, on error I don't need just to change a border color, but to show or not different elements of the widget template and the position of some of them change.
I already modify the based widget to add errors, but I'm looking to do it in a more elegant way at the field level by passing from the field to the widget, parameters depending on error type.
So my question is what I need to overwrite to pass from field to widget errors and other variables ?
Not sure whether this could help in your specific use case ... but just in case, please note that when you build your form in the view, you can add extra parameters as needed, then pass them down to your custom widget.
Working example:
file "forms.py"
from django import forms
def build_ingredient_form(unit):
"""
Ingredient form factory
Here we build the form class dynamically, in order to acces 'unit' via closure.
References:
http://stackoverflow.com/questions/622982/django-passing-custom-form-parameters-to-formset#623030
"""
class IngredientForm(forms.Form):
#quantity = forms.DecimalField(max_digits=10)
quantity = UnitField(unit, required=False)
...
return IngredientForm
file "fields.py"
from django import forms
from .fields import UnitField
class UnitField(forms.CharField):
"""
Custom field to support UnitWidget
References:
- http://tothinkornottothink.com/post/10815277049/django-forms-i-custom-fields-and-widgets-in
"""
def __init__(self, unit, *args, **kwargs):
self.unit = unit
super(UnitField, self).__init__(*args, **kwargs)
self.widget = UnitWidget(unit)
...
file "widgets.py"
from django import forms
from .models import Unit
class UnitWidget(forms.TextInput):
def __init__(self, unit, attrs=None):
if unit is None:
self.unit = Unit()
else:
self.unit = unit
...
Well a widget is how you will render the field's data/value into the HTML rendered template, that's the only function of widgets, look the following example taken from the docs:
>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required>'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name">'
So, widgets are not aware of the data is valid(has errors) or not and should remain that way.
Is not a good idea to handle any data error/validation at the widget level, you want, I can ensure that, that if you change how your field looks like (the widget), your validations keeps working.
Said that ...
How can I access field errors?
When you are rendering a form you can do it field by field lets take this form by example:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
you can write to temlate:
<form action="." method="get">
<p>{{ loginform.username.label }}: {{ loginform.username }}</p>
<p>{{ loginform.password.label }}: {{ loginform.password}}</p>
<button type="submit">submit</button>
</form>
And this will render something like the following:
Now, suppose your form won't admit passwords with less than 8 characters:
class LoginForm(forms.Form):
username = forms.CharField(max_length=255)
password = forms.CharField(widget=forms.PasswordInput)
def clean_password(self):
password = self.cleaned_data['password']
if len(password) < 8:
raise forms.ValidationError(
"Password must have at least 8 characters, it has only %(password_length)s",
code='invalid password',
params={'password_length': len(password)}
)
return password
You can access the password errors like this:
<form action="." method="get">
{% csrf_token %}
<p>{{ form.username.label }}: {{ form.username }}</p>
<p>{{ form.password.label }}: {{ form.password}}</p>
<ul>
{% for error in form.password.errors %}
<li>{{ error }}</li>
{% endfor %}
</ul>
<button type="submit">submit</button>
</form>
And now if you type a short password ...
I want the control to look different if there are errors.
You can add some style if there are errors just use {% if ... %} in your template code:
<p>
{{ form.password.label }}:
<span class="{% if form.password.errors %}pass_error{% endif %}">
{{ form.password }}
</span>
</p>
With CSS:
<style>
.pass_error input {
border-color: red;
}
</style>
And this is the result:
Conlusion.
Validate and handle data errors in the form or using validators, use widgets for display the data, of course, you can customize how the data is presented since you can specify a custom template for your widget.
I also recommend django-widget-twaeks if you want to add attributes to your widget in template code. This apps allows you to write code like (example from the app docs):
{% load widget_tweaks %}
<!-- change input type (e.g. to HTML5) -->
{% render_field form.search_query type="search" %}
<!-- add/change several attributes -->
{% render_field form.text rows="20" cols="20" title="Hello, world!" %}
<!-- append to an attribute -->
{% render_field form.title class+="css_class_1 css_class_2" %}
<!-- template variables can be used as attribute values -->
{% render_field form.text placeholder=form.text.label %}
I want to format my modelforms with bootstrap, and without any additional packages (just using the bootstrap source files). A particular form that I want configured:
class FoodForm(forms.ModelForm):
class Meta:
model = Food
fields = ['name', 'company']
exclude = ('user', 'edit')
The 'name' is a text field I'd want to be a bootstrap text field, and 'company' is a selection field (from a foreign key) that I'd want to be a bootstrap dropdown.
The current setup of the form template:
{% extends "mainsite/base.html" %}
{% block content %}
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-primary">Save</button>
</form>
</form>
{% endblock %}
What's best practice for formatting any django modelform field into bootstrap?
The trick to bootstrap fields is injecting the form-control class into each field, and making sure each field lives inside a form-group dom element. To inject that class into each one of your fields, you could so something like:
class FoodForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(FoodForm, self).__init__(*args, **kwargs)
for field in iter(self.fields):
self.fields[field].widget.attrs.update({
'class': 'form-control'
})
Now, in your django template you can iterate through your forms fields, placing each one into a bootstrap'd form-group. For example:
<form method="POST" class="post-form">
{% for field in form %}
<div class="form-group">
{{ field }}
</div>
{% endfor %}
</form>
I'll also say that this setup (Django + Bootstrap) is super common now adays, so googling "Bootstrap forms with django" should yield a wealth of knowledge on customizing this even further. Good luck!
Simply use Django Crispy Forms
It is fairly easy and straight forward
Extreme noob here. I have been trying to create a simple form in Django where the user can select from the models that are present in the database and then click submit (whereupon I will then return a list of objects).
However, I am getting the following error: 'ModelBase' object is not iterable
Note all I am trying to achieve so far is to actually render the form.
Here is the code:
HTML
<form action="." method="">
{% csrf_token %}
{% for field in output %}
{{ output.as_p }}
{% endfor %}
<input type="submit" value="Save" />
</form>
Forms.py
from projectdb.models import *
class TablesForm(forms.Form):
models = models.get_models()
select = forms.ChoiceField(choices=models)
Views.py
def output_form(request):
form = TablesForm()
return render(request, 'projectdb/output.html', {'output': form})
Much obliged for any help!
Some object is not iterable errors will be fixed by adding .all() where you're doing the foreach loop. If the loop is in the template, try .all only
In a view:
for something in array.all():
In a template:
{% for field in output.all %}
And everytime I do a form in Django my method on the form is empty method="". This let's you return to the same view and process your data there. If you have errors on the form you can redirect to the same form but with the pre-saved data that the user has wrote.
Hope it helps.
I feel like I must be missing something obvious but I am having a problem where my model formsets insist on retaining their data after submission. I am creating a page that allows a user to create a project and then add an arbitrary amount of materials to that project. JavaScript is taking care of dynamically adding new instances of the formset as needed. The code works fine the first time through, after that it "remembers" previous data. It happens for the material formset but not for the regular model form above it.
I'm thinking it must have something to do with the way I am creating my model formset. When the page is requested the view seems to be passing back the formset bound to old data rather than an unbound set. I'm new to Django and am trying to teach myself so there are probably things at work I do not quite grasp yet. Below is the code for the view:
def addproject_page(request):
# Define the formset to use to add the materials
MaterialFormSet = modelformset_factory(Material, exclude = ('project',))
# Check to see if there is some POST data from an attempt to fill out the form
if request.method == 'POST':
# Create a form for the project and for the material and use a prefix to separate the POST data for the two
project_form = ProjectForm(request.POST, prefix='project')
# Instantiate the formset to display multiple materials when creating a project
material_formset = MaterialFormSet(request.POST, prefix='material')
# Check both forms with the validators and if both are good process the data
if project_form.is_valid() and material_formset.is_valid():
# Save the data for the newly created project
created_project = project_form.save()
# Tell each material to be associated with the above created project
instances = material_formset.save(commit=False)
for instance in instances:
instance.project = created_project
instance.save()
# After the new project and its materials are created, go back to the main project page
return HttpResponseRedirect('/members/projects/')
# If there is no post data, create and show the blank forms
else:
project_form = ProjectForm(prefix='project')
material_formset = MaterialFormSet(prefix='material')
return render(request, 'goaltracker/addproject.html', {
'project_form': project_form,
'material_formset': material_formset,
})
Edit to add in my template code too in case it helps:
{% extends "base.html" %}
{% block external %}
<script src="{{ static_path }}js/projects.js" type="text/javascript"></script>
{% endblock %}
{% block title %}: Add Project{% endblock %}
{% block content %}
<h1>Add a Project</h1>
<form id="new_project_form" method="post" action="">
{{ project_form.as_p }}
<!-- The management form must be rendered first when iterating manually -->
<div>{{ material_formset.management_form }}</div>
<!-- Show the initial blank form(s) before offering the option to add more via JavaScript -->
{% for material_form in material_formset.forms %}
<div>{{ material_form.as_p }}</div>
{% endfor %}
<input type="button" value="Add Material" id="add_material">
<input type="button" value="Remove Material" id="remove_material">
<input type="submit" value="add" />
</form>
{% endblock %}
I think you need to use a custom queryset, so that your formset is instantiated with an empty queryset. You need to specify the queryset in the POST and GET branches of your if statement.
if request.method == "POST":
...
material_formset = MaterialFormSet(request.POST, prefix='material', queryset=Material.objects.none())
...
else:
material_formset = MaterialFormSet(prefix='material', queryset=Material.objects.none())
At the moment, your formset is using the default queryset, which contains all objects in the model.
Answer of the question the old data is always persists in modelformset is here. https://docs.djangoproject.com/en/1.8/topics/forms/modelforms/#changing-the-queryset as it is given in the docs chenge the queryset by overriding the constructor of the basemodelformset
from django.forms.models import BaseModelFormSet
from myapp.models import Author
class CalendarFormset(BaseModelFormSet):
def __init__(self, *args, **kwargs):
super(CalendarFormset, self).__init__(*args, **kwargs)
self.queryset = Calendar.objects.none()
A same problem was discussed here
django modelformset_factory sustains the previously submitted data even after successfully created the objects