Here is an example from the model:
class Shipment(models.Model):
shipment_id = models.BigAutoField(null=False, primary_key=True)
potential_shipping_dates = ArrayField(models.DateField(), verbose_name='Ship Dates', null=True)
Here is what I'm sort of attempting in my form:
class ShippingForm(forms.Form):
potential_shipping_dates = forms.ModelChoiceField(queryset=Shipment.objects.all())
def __init__(self, *args, **kwargs):
super(ShippingForm, self).__init__(*args, **kwargs)
And here is where my form is added to context:
context['shippingForm'] = ShippingForm(initial=??what_goes_here_maybe??)
My form renders fine but I want to show a dropdown with a date for each option.
Okay this is a bit complex, but I think I understand what you're trying to do, and where you're going wrong.
So you have a Shipment model, and each Shipment instance has a field with a few different potential_shipping_dates.
Say you have 2 shipments:
IN : ship1 = Shipment.objects.first()
OUT:
IN : ship1.potential_shipping_dates
OUT: ['01/01/2021', '02/02/2021']
IN : ship2 = Shipment.objects.last()
OUT:
IN : ship2.potential_shipping_dates
OUT: ['03/03/2021', '04/04/2021']
Now, do you want the dropdown to have all 4 dates as possibilities, and that will select the Shipment?
Or do you want to select a date after selecting the shipment in the form?
^^ Answered in comments
Okay so you will need to pass the instance through to the form:
views.py
# Inherit from Django's UpdateView to have `instance` passed through to the form
class ShippingFormView(UpdateView):
model = Shipment
form_class = ShippingForm
# Or if you don't want to inherit from inherit from UpdateView
class ShippingFormView(Blah):
model = Shipment
form_class = ShippingForm
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['instance'] = self.get_object()
return kwargs
# Or if you're using function based views
def shipping_form_view(request, pk):
shipment = get_object_or_404(Shipment, pk=pk)
form = ShippingForm(request, instance=shipment)
...
forms.py
class ShippingForm(forms.Form):
potential_shipping_dates = forms.ChoiceField(choices=[])
def __init__(self, *args, instance, **kwargs):
super(ShippingForm, self).__init__(*args, **kwargs)
self.fields['potential_shipping_dates'].choices = ((dt, dt) for dt in instance.potential_shipping_dates)
ModelChoiceFields are used when selecting an object, not an attribute on one.
i'm taking values from database table in views file and has to render those values to a form in template file which is created by using the forms class and i have to show those values for some fields and make them immutable.
class OrderForm(forms.Form):
pid=forms.IntegerField()
pname=forms.CharField()
pprice=forms.FloatField()
person_name=forms.CharField(max_length=40)
emailid=forms.EmailField()
address=forms.CharField(max_length=40)
city=forms.CharField(max_length=20)
state=forms.CharField(max_length=20)
zip=forms.IntegerField()
card=forms.IntegerField()
exp= forms.DateField()
cvv=forms.IntegerField()
def order(request,pk):
pid=pk
user_name=request.user.username
qs=Product.objects.get(pid=pid)
pname=qs.pname.format()
list={'username':user_name,'pid':pid,'pname':pname}
form=OrderForm
return render(request,'order.html',{'data':list,'form':form})
i expect input filed with value that i passed by default which is immutable and when i submit i have to get same value i passed
Looks to me like you're better off using a ModelForm. It would be something like:
class OrderForm(forms.ModelForm)
class Meta:
model = Order
widgets = {
`immutable_field` : forms.TextInput(attrs={'readonly':True})
}
def order(request,pk):
pid=pk
user_name=request.user.username
qs=Product.objects.get(pid=pid)
pname=qs.pname.format()
list={'username':user_name,'pid':pid,'pname':pname}
form=OrderForm()
form.fields['immutable_field'] = "Some Value"
return render(request,'order.html',{'data':list,'form':form})
If you already have an order then you can prepopulate the fields with form=OrderForm(instance=order)
Make field as disable from Form init method and pass initial value from view section
class OrderForm(forms.Form):
pid=forms.IntegerField()
pname=forms.CharField()
pprice=forms.FloatField()
person_name=forms.CharField(max_length=40)
emailid=forms.EmailField()
address=forms.CharField(max_length=40)
city=forms.CharField(max_length=20)
state=forms.CharField(max_length=20)
zip=forms.IntegerField()
card=forms.IntegerField()
exp= forms.DateField()
cvv=forms.IntegerField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].disabled = True
self.fields['pid'].disabled = True
self.fields['pname'].disabled = True
Here in view you can pass dictionary to form as initial value of fields.
def order(request,pk):
pid=pk
user_name=request.user.username
qs=Product.objects.get(pid=pid)
pname=qs.pname.format()
initial={'username':user_name,'pid':pid,'pname':pname}
form=OrderForm(initial=initial)
return render(request,'order.html',{'data':initla,'form':form})
I would like to have the values for latitude and longitude to always display a dot (".") instead of a comma (",") when showing the latitude and longitude form fields.
This seems to be tricky with crispy forms.
In the template which shows the model's fields I just use
{% crispy form %}
But I did not find in the documentation of crispy forms how to do sth. like
{{ value|unlocalize }}
as provided by the Django documentation. Since the crispy forms is supposed to be generic as in the following code example, I don't know where to set the trigger.
extract from forms.py
class CrispyForm(ModelForm):
"""
This form serves as a generic form for creating and updating items.
"""
helper = None
def __init__(self, cancel_button, *args, **kwargs):
form_action = kwargs.pop('form_action', None)
model_name = kwargs.pop('model_name', None)
super(CrispyForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
if form_action is not None:
action = reverse(form_action)
else:
action = ""
# Form attributes
self.helper.form_method = 'post'
self.helper.form_action = action
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-lg-2'
self.helper.field_class = 'col-lg-10'
# Save button, having an offset to align with field_class
save_text = _('Save %(model)s') % {'model': model_name}
cancel_text = _('Cancel')
self.helper.layout.append(Submit('save_form', save_text, css_class="btn btn-primary col-sm-offset-2 save_item"))
self.helper.layout.append(Submit('cancel', cancel_text, css_class="btn btn-primary"))
and here is a form which has model fields latitude and longitude
class SomeItemCreateForm(CrispyForm):
def __init__(self, *args, **kwargs):
kwargs['form_action'] = 'create_someitem_url'
kwargs['model_name'] = self._meta.model._meta.verbose_name
super(SomeItemCreateForm, self).__init__(False, *args, **kwargs)
class Meta:
model = SomeItem
fields = '__all__'
The SomeItem model has a longitude and latitude field amongst others.
Check out the Layout Docs
You'll basically want to create a custom template for your field and then use that.
Your code will be a bit like this:
form = SomeItemCreateForm(...)
form.helper.layout = Layout(
Field('latitude', template='custom_field_template.html'),
Field('longitude', template='custom_field_template.html')
)
I hope that helps.
I want to re-use a template I have with my WTForms form:
<th>${form.name.label}</th>
<td>${form.name()}</td>
...
However, on my edit page, I want the input fields to display as normal (TextField, SelectField, etc.), while on my view page, I want to just display the value of the property, not the input field with the value.
Edit page:
<th>Name:</th>
<td><input type="text" value="Current Name" name="name" id="name"/></td>
View page:
<th>Name:</th>
<td>Current Name</td>
I know I can access a field's value via form.name.data, but is there any way I can keep the same template with form.name() being called and somehow toggle whether that outputs <input type="text"... or Current Name?
I created a custom widget:
from wtforms.fields import Field
class PlainTextWidget(object):
def __call__(self, field, **kwargs):
return field.data if field.data else ''
Then, for my view page, I added the following:
form = MyForm(obj=myDataRow)
fields = [val for val in form._fields]
for fieldName in fields:
fieldProp = getattr(form, fieldName)
setattr(fieldProp, 'widget', PlainTextWidget())
Sarah's answer above led me to the solution to a related problem: What if you want some of your fields to be read only? In that case, instead of doing run-time surgery on the form object, you could define a new ROTextField variant (for example), that always renders to the pure value. For example:
from wtforms.widgets import Input
from wtforms.fields import StringField
class TextOutput(Input):
def __call__(self, field, **kwargs):
return kwargs.get('value', field._value())
class ROTextField(StringField):
widget = TextOutput()
Now define your field with ReadOnly attributes:
class UserPrefs(Form):
name = ROTextField('name', default='Jon')
# ...
Thinking about this problem helped me better understand how WTForms work. Leaving this here in case this might help someone else work through related issues.
Based on Sarah's answer and code found in WTForms-Components I use the following to quickly turn all a form's fields into read-only and disabled fields.
Suppose we have a ProfileForm defined as follows:
class ProfileEditForm(Form):
title = StringField("Title", validators=[validators.required("Please enter your title.")])
first_name = StringField("First Name", validators=[validators.required("Please enter your first name.")])
middle_name = StringField("Middle Name")
last_name = StringField("Last Name", validators=[validators.required("Please enter your last name.")])
organisation = StringField("Company Name", validators=[validators.required("Please enter your company name.")])
organisation_website = StringField("Company Website")
# more fields ...
Define the following class (based on ReadOnlyWidgetProxy from WTForms-Components):
class ReadOnlyAndDisabledWidgetProxy(object):
def __init__(self, widget):
self.widget = widget
def __getattr__(self, name):
return getattr(self.widget, name)
def __call__(self, field, **kwargs):
kwargs.setdefault('readonly', True)
kwargs.setdefault('disabled', True)
return self.widget(field, **kwargs)
Now inherit from ProfileForm as follows:
class ReadOnlyProfileForm(ProfileForm):
def __init__(self, *args, **kwargs):
super(ReadOnlyProfileForm, self).__init__(*args, **kwargs)
for field_name in self._fields:
field_property = getattr(self, field_name)
field_property.widget = ReadOnlyAndDisabledWidgetProxy(field_property.widget)
I'm using Django forms in my website and would like to control the order of the fields.
Here's how I define my forms:
class edit_form(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class create_form(edit_form):
name = forms.CharField()
The name is immutable and should only be listed when the entity is created. I use inheritance to add consistency and DRY principles. What happens which is not erroneous, in fact totally expected, is that the name field is listed last in the view/html but I'd like the name field to be on top of summary and description. I do realize that I could easily fix it by copying summary and description into create_form and loose the inheritance but I'd like to know if this is possible.
Why? Imagine you've got 100 fields in edit_form and have to add 10 fields on the top in create_form - copying and maintaining the two forms wouldn't look so sexy then. (This is not my case, I'm just making up an example)
So, how can I override this behavior?
Edit:
Apparently there's no proper way to do this without going through nasty hacks (fiddling with .field attribute). The .field attribute is a SortedDict (one of Django's internal datastructures) which doesn't provide any way to reorder key:value pairs. It does how-ever provide a way to insert items at a given index but that would move the items from the class members and into the constructor. This method would work, but make the code less readable. The only other way I see fit is to modify the framework itself which is less-than-optimal in most situations.
In short the code would become something like this:
class edit_form(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class create_form(edit_form):
def __init__(self,*args,**kwargs):
forms.Form.__init__(self,*args,**kwargs)
self.fields.insert(0,'name',forms.CharField())
That shut me up :)
From Django 1.9+
Django 1.9 adds a new Form attribute, field_order, allowing to order the field regardless their order of declaration in the class.
class MyForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
author = forms.CharField()
notes = form.CharField()
field_order = ['author', 'summary']
Missing fields in field_order keep their order in the class and are appended after the ones specified in the list. The example above will produce the fields in this order: ['author', 'summary', 'description', 'notes']
See the documentation: https://docs.djangoproject.com/en/stable/ref/forms/api/#notes-on-field-ordering
Up to Django 1.6
I had this same problem and I found another technique for reordering fields in the Django CookBook:
class EditForm(forms.Form):
summary = forms.CharField()
description = forms.CharField(widget=forms.TextArea)
class CreateForm(EditForm):
name = forms.CharField()
def __init__(self, *args, **kwargs):
super(CreateForm, self).__init__(*args, **kwargs)
self.fields.keyOrder = ['name', 'summary', 'description']
From Django 1.9: https://docs.djangoproject.com/en/1.10/ref/forms/api/#notes-on-field-ordering
Original answer: Django 1.9 will support this by default on the form with field_order:
class MyForm(forms.Form):
...
field_order = ['field_1', 'field_2']
...
https://github.com/django/django/commit/28986da4ca167ae257abcaf7caea230eca2bcd80
I used the solution posted by Selene but found that it removed all fields which weren't assigned to keyOrder. The form that I'm subclassing has a lot of fields so this didn't work very well for me. I coded up this function to solve the problem using akaihola's answer, but if you want it to work like Selene's all you need to do is set throw_away to True.
def order_fields(form, field_list, throw_away=False):
"""
Accepts a form and a list of dictionary keys which map to the
form's fields. After running the form's fields list will begin
with the fields in field_list. If throw_away is set to true only
the fields in the field_list will remain in the form.
example use:
field_list = ['first_name', 'last_name']
order_fields(self, field_list)
"""
if throw_away:
form.fields.keyOrder = field_list
else:
for field in field_list[::-1]:
form.fields.insert(0, field, form.fields.pop(field))
This is how I'm using it in my own code:
class NestableCommentForm(ExtendedCommentSecurityForm):
# TODO: Have min and max length be determined through settings.
comment = forms.CharField(widget=forms.Textarea, max_length=100)
parent_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
def __init__(self, *args, **kwargs):
super(NestableCommentForm, self).__init__(*args, **kwargs)
order_fields(self, ['comment', 'captcha'])
It appears that at some point the underlying structure of field order was changed from a django specific SordedDict to a python standard OrderedDict
Thus, in 1.7 I had to do the following:
from collections import OrderedDict
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
original_fields = self.fields
new_order = OrderedDict()
for key in ['first', 'second', ... 'last']:
new_order[key] = original_fields[key]
self.fields = new_order
I'm sure someone could golf that into two or three lines, but for S.O. purposes I think clearly showing how it works is better than cleaver.
You could also create a decorator to order fields (inspired by Joshua's solution):
def order_fields(*field_list):
def decorator(form):
original_init = form.__init__
def init(self, *args, **kwargs):
original_init(self, *args, **kwargs)
for field in field_list[::-1]:
self.fields.insert(0, field, self.fields.pop(field))
form.__init__ = init
return form
return decorator
This will ensure that all the fields passed to the decorator come first.
You can use it like this:
#order_fields('name')
class CreateForm(EditForm):
name = forms.CharField()
The accepted answer's approach makes use of an internal Django forms API that was changed in Django 1.7. The project team's opinion is that it should never have been used in the first place. I now use this function to reorder my forms. This code makes use of an OrderedDict:
def reorder_fields(fields, order):
"""Reorder form fields by order, removing items not in order.
>>> reorder_fields(
... OrderedDict([('a', 1), ('b', 2), ('c', 3)]),
... ['b', 'c', 'a'])
OrderedDict([('b', 2), ('c', 3), ('a', 1)])
"""
for key, v in fields.items():
if key not in order:
del fields[key]
return OrderedDict(sorted(fields.items(), key=lambda k: order.index(k[0])))
Which I use in classes like this:
class ChangeOpenQuestionForm(ChangeMultipleChoiceForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
key_order = ['title',
'question',
'answer',
'correct_answer',
'incorrect_answer']
self.fields = reorder_fields(self.fields, key_order)
For recent versions of Django (>=1.9), see the other answers' Form.field_order
See the notes in this SO question on the way Django's internals keep track of field order; the answers include suggestions on how to "reorder" fields to your liking (in the end it boils down to messing with the .fields attribute).
Alternate methods for changing the field order:
Pop-and-insert:
self.fields.insert(0, 'name', self.fields.pop('name'))
Pop-and-append:
self.fields['summary'] = self.fields.pop('summary')
self.fields['description'] = self.fields.pop('description')
Pop-and-append-all:
for key in ('name', 'summary', 'description'):
self.fields[key] = self.fields.pop(key)
Ordered-copy:
self.fields = SortedDict( [ (key, self.fields[key])
for key in ('name', 'summary' ,'description') ] )
But Selene's approach from the Django CookBook still feels clearest of all.
Based on an answer by #akaihola and updated to work with latest Django 1.5 as self.fields.insert is being depreciated.
from easycontactus.forms import *
from django import forms
class CustomEasyContactUsForm(EasyContactUsForm):
### form settings and configuration
CAPTHCA_PHRASE = 'igolf'
### native methods
def __init__(self, *args, **kwargs):
super(CustomEasyContactUsForm, self).__init__(*args, **kwargs)
# re-order placement of added attachment field
self.fields.keyOrder.insert(self.fields.keyOrder.index('captcha'),
self.fields.keyOrder.pop(self.fields.keyOrder.index('attachment'))
)
### field defintitions
attachment = forms.FileField()
In the above we are extending an EasyContactUsForm base class as it is defined in django-easycontactus package.
I built a form 'ExRegistrationForm' inherited from the 'RegistrationForm' from Django-Registration-Redux. I faced two issues, one of which was reordering the fields on the html output page once the new form had been created.
I solved them as follows:
1. ISSUE 1: Remove Username from the Registration Form: In my_app.forms.py
class ExRegistrationForm(RegistrationForm):
#Below 2 lines extend the RegistrationForm with 2 new fields firstname & lastname
first_name = forms.CharField(label=(u'First Name'))
last_name = forms.CharField(label=(u'Last Name'))
#Below 3 lines removes the username from the fields shown in the output of the this form
def __init__(self, *args, **kwargs):
super(ExRegistrationForm, self).__init__(*args, **kwargs)
self.fields.pop('username')
2. ISSUE 2: Make FirstName and LastName appear on top: In templates/registration/registration_form.html
You can individually display the fields in the order that you want. This would help in case the number of fields are less, but not if you have a large number of fields where it becomes practically impossible to actually write them in the form.
{% extends "base.html" %}
{% load i18n %}
{% block content %}
<form method="post" action=".">
{% csrf_token %}
#The Default html is: {{ form.as_p }} , which can be broken down into individual elements as below for re-ordering.
<p>First Name: {{ form.first_name }}</p>
<p>Last Name: {{ form.last_name }}</p>
<p>Email: {{ form.email }}</p>
<p>Password: {{ form.password1 }}</p>
<p>Confirm Password: {{ form.password2 }}</p>
<input type="submit" value="{% trans 'Submit' %}" />
</form>
{% endblock %}
The above answers are right but incomplete. They only work if all the fields are defined as class variables. What about dynamic form fields which have to be defined in the intitialiser (__init__)?
from django import forms
class MyForm(forms.Form):
field1 = ...
field2 = ...
field_order = ['val', 'field1', 'field2']
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
The above will never work for val but will work for field1 and field2 (if we reorder them). You might want to try defining field_order in the initialiser:
class MyForm(forms.Form):
# other fields
def __init__(self, val_list, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
vals = zip(val_list, val_list)
self.fields['val'] = forms.CharField(choices=vals)
self.field_order = ['val', 'field1', 'field2']
but this will also fail because the field order is fixed before the call to super().
Therefore the only solution is the constructor (__new__) and set field_order to a class variable.
class MyForm(forms.Form):
# other fields
field_order = ['val', 'field1', 'field2']
def __new__(cls, val_list, *args, **kwargs):
form = super(MyForm, cls).__new__(cls)
vals = zip(val_list, val_list)
form.base_fields['val'] = forms.CharField(choices=vals)
return form