====== Django Forms ======
===== Render a form =====
Fíjate aquí, con ''novalidate'' no haría la validación de datos local.
==== How does it work? ====
When we write ''{{ form }}'' in a template, it’s actually accessing the ''__str__'' method from the BaseForm class. We can use other three methods to render it (as table, as p, as ul tags): as_table(), as_ul(), as_p().
The as_table() and as_ul() methods do not create the
and the
tags, so we have to add it by ourselves.
==== The form definition ====
class ColorfulContactForm(forms.Form):
name = forms.CharField(
max_length=30,
widget=forms.TextInput(
attrs={
'style': 'border-color: blue;',
'placeholder': 'Write your name here'
}
)
)
email = forms.EmailField(
max_length=254,
widget=forms.EmailInput(attrs={'style': 'border-color: green;'})
)
message = forms.CharField(
max_length=2000,
widget=forms.Textarea(attrs={'style': 'border-color: orange;'}),
help_text='Write here your message!'
)
==== The validation process... ====
Converts all the data to proper values in ''cleaned_data'' if they are, for sure, valid. An example of a Boolean field:
class MyForm(forms.Form):
has_code = forms.BooleanField(widget= forms.CheckboxInput())
def my_form_view(request):
if request.method == 'POST':
my_form = MyForm(request.POST)
if my_form.is_valid():
# my_form.cleaned_data['has_code'] is now a bool
===== Gotchas =====
unique_together = ('accounting_year', 'number',)
===== Widgets =====
You can define widgets on for the fields on the same form:
class PurchaseOrderForm(forms.ModelForm):
class Meta:
model = PurchaseOrder
fields = '__all__'
widgets = {
'bill_price': EuroWidget(),
'total_amount': EuroWidget(),
}
==== Add a widget to a field ====
city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))
También lo puedes añadir en el meta:
'widgets': {'city': forms.TextInput}
===== Fields =====
==== Choice ====
=== From DB ===
IF you are saving cities into the database, you can use ModelChoiceField:
class SelectCityForm(forms.Form):
cities = forms.ModelChoiceField(queryset=City.objects.all())
==== Create your own field ====
You need the field and the widget:
class SelectMultipleChecks(forms.Widget):
template_name = 'widgets/multiple_checks.html'
def __init__(self, *args, choices=(), defaults=(), **kwargs):
super().__init__(*args, **kwargs)
self.choices = choices
def get_context(self, name, value, attrs):
context = super().get_context(name, value, attrs)
context['choices'] = self.choices
return context
class SelectMultipleChecksField(forms.Field):
widget = SelectMultipleChecks
def __init__(self, *args, choices=(), defaults=(), **kwargs):
super().__init__(*args, **kwargs)
self.choices = choices
def _get_choices(self):
return self._choices
def _set_choices(self, value):
if callable(value):
value = CallableChoiceIterator(value)
else:
value = list(value)
self._choices = self.widget.choices = value
choices = property(_get_choices, _set_choices)
def to_python(self, value):
import json
values = json.loads(value)
i_values = [int(v) for v in values]
return [choice for choice in self._choices if choice.pk in i_values]
def validate(self, value):
assert len(value) > 0
def valid_value(self, value):
return value
With this we can have the template ''widgets/multiple_checks.html'':
{% for choice in choices %}
{{ choice }}
{% endfor %}
=== Set rendered properties ===
You can set properties to render like this:
city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'}))