Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
|
wiki2:python:django:forms [2018/11/10 17:54] alfred [Render a form] |
wiki2:python:django:forms [2020/05/09 09:25] (actual) |
||
|---|---|---|---|
| Línea 88: | Línea 88: | ||
| <button type="submit">Submit</button> | <button type="submit">Submit</button> | ||
| </form> | </form> | ||
| + | </code> | ||
| + | |||
| + | ==== The form definition ==== | ||
| + | <code python> | ||
| + | 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!' | ||
| + | ) | ||
| + | </code> | ||
| + | |||
| + | ==== 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: | ||
| + | <code python> | ||
| + | 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 | ||
| + | </code> | ||
| + | |||
| + | ===== Gotchas ===== | ||
| + | |||
| + | <code python> | ||
| + | unique_together = ('accounting_year', 'number',) | ||
| + | </code> | ||
| + | ===== Widgets ===== | ||
| + | You can define widgets on for the fields on the same form: | ||
| + | <code> | ||
| + | class PurchaseOrderForm(forms.ModelForm): | ||
| + | class Meta: | ||
| + | model = PurchaseOrder | ||
| + | fields = '__all__' | ||
| + | widgets = { | ||
| + | 'bill_price': EuroWidget(), | ||
| + | 'total_amount': EuroWidget(), | ||
| + | } | ||
| + | </code> | ||
| + | |||
| + | ==== Add a widget to a field ==== | ||
| + | <code> | ||
| + | city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'})) | ||
| + | </code> | ||
| + | También lo puedes añadir en el meta: | ||
| + | <code> | ||
| + | 'widgets': {'city': forms.TextInput} | ||
| + | </code> | ||
| + | ===== Fields ===== | ||
| + | ==== Choice ==== | ||
| + | === From DB === | ||
| + | IF you are saving cities into the database, you can use ModelChoiceField: | ||
| + | <code> | ||
| + | class SelectCityForm(forms.Form): | ||
| + | cities = forms.ModelChoiceField(queryset=City.objects.all()) | ||
| + | </code> | ||
| + | |||
| + | |||
| + | ==== Create your own field ==== | ||
| + | You need the field and the widget: | ||
| + | |||
| + | <code python> | ||
| + | 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 | ||
| + | </code> | ||
| + | |||
| + | With this we can have the template ''widgets/multiple_checks.html'': | ||
| + | <code html> | ||
| + | <div style="display: flex; justify-content: space-around;"> | ||
| + | <input type="hidden" id="{{ widget.attrs.id }}" value="[{{ selected|join:"," }}]"> | ||
| + | {% for choice in choices %} | ||
| + | <div><input | ||
| + | type="checkbox" {% if choice.pk in selected %}checked{% endif %} | ||
| + | name="{{widget.attrs.id}}_check" | ||
| + | data-id="{{choice.pk}}" | ||
| + | > {{ choice }}</div> | ||
| + | {% endfor %} | ||
| + | </div> | ||
| + | </code> | ||
| + | |||
| + | === Set rendered properties === | ||
| + | |||
| + | |||
| + | You can set properties to render like this: | ||
| + | <code> | ||
| + | city = forms.CharField(widget=forms.TextInput(attrs={'autocomplete':'off'})) | ||
| </code> | </code> | ||