Django自带了表单功能,可以与前端及后端完美集成,能非常方便地提供创建、更新或删除模型。
表单定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| class Post(models.Model): name = models.CharField() subtitle = models.CharField(blank=True) content = models.TextField() created_at = models.DatetimeField() user = models.ForeignField(User)
class PostForm(forms.ModelForm): error_css_class = 'error' required_css_class = 'required' another_field = forms.DateField( required=True, label="Custom Field Name", label_suffix=":", initial="abc", help_text="除Model本身字段以外的额外的字段", error_messages=[] validators=[] disabled=False widget={ } ) choice_field = forms.ModelChoiceField(queryset=MyChoices.objects.all())
class Meta: model = Post fields = ("name", "subtitle", "content", "another_field") labels = {"name": "Input the Name"} help_texts = {"name": "Enter a correct name"} error_messages = {"name": { "max_length": "字段太长了" }} field_classes = { "name": "my_text" } widgets = { "created_at": forms.TextInput(attrs={ "type": "date", "class": "my-class custom-class" "placeholder": "设置placeholder" }), }
def __init__(self, *args, **kwargs): super(PostForm, self).__init__(*args, **kwargs) self.fields["name"].label = "Post Name" self.fields['user'].queryset = User.objects.filter(id__in[1,2,3]) self.fields['my_choice_field'] = forms.ChoiceField(choices=[1,2,3]) def clean_name(self): data = self.cleaned_data['name'] if 'test' in data: raise ValidationError(_('Invalid name')) return data
|
表单支持的字段类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| BooleanField CharField ChoiceField TypedChoiceField DateField DateTimeField DecimalField DurationField EmailField FileField FilePathField FloatField ImageField IntegerField GenericIPAddressField MultipleChoiceField TypedMultipleChoiceField NullBooleanField RegexField SlugField TimeField URLField UUIDField
|
实现动态choice字段
- 如果一个choice的可选值需要通过另一个用户的输入来决定,可以像这样动态决定其queryset,例如地区选择字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| class DynamicModelChoiceField(ModelChoiceField): def to_python(self, value): try: value = super(DynamicModelChoiceField, self).to_python(value) except ValidationError: key = self.to_field_name or "pk" value = self.queryset.model.objects.filter(**{key: value}) if not value.exists(): raise else: value = value.first() return value
class MyForm(forms.ModelForm): def __init__(self, *args, **kwargs): self.fields['abc'] = DynamicModelChoiceField(queryset=States.objects.filter(country=self.instance.country))
|
自定义model的展示名称
- 复写Field的
label_from_instance
方法能够自定义model的展示名称,而不是直接用Model的__str__
1 2 3 4
| class UserModelChoiceField(ModelChoiceField): def label_from_instance(self, obj): return obj.firstname + obj.lastname UserModelChoiceField(queryset=User.objects.all())
|
应用表单
模板显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <form enctype="multipart/form-data" method='POST'> {% csrf_token %} {{ form }} # 手动挨个呈现字段 <div class="row"> <div class="col-6"> {{ form.name }} </div> <div class="col-6"> {{ form.email }} </div> </div> <button type="submit" class="btn btn-primary">Submit</button> </form>
|
后端保存
1 2 3 4 5 6 7 8 9 10
| form = PostForm(request.POST, request.FILES) if form.is_valid(): form.has_changed() obj = form.save(commit=False) obj.user = request.user obj.save() else: print(form.errors) print(form.errors.as_json()) form.has_error(field_name)
|
使用django-crispy-forms
可以让表单自动支持Bootstrap4
,看起来更漂亮。
安装与配置非常简单:
1 2 3 4 5 6 7
| pip install django-crispy-forms INSTALLED_APPS = [..., 'crispy_forms'] # 添加到INSTALLED_APPS中 CRISPY_TEMPLATE_PACK = 'bootstrap4' # 使用bootstrap4,当然也可以使用旧版本
# 当然,依然要在html中引入css和js,可以在一个base.html中引入 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"></script>
|
在需要美化表单的时候直接这样做:
1 2 3 4 5 6 7 8 9 10
| {% extends 'base.html' %}
{% load crispy_forms_tags %} <form enctype="multipart/form-data" method='POST'> {% csrf_token %} {{ form|crispy }} {{ form.name|as_crispy_field}} # 或者这样可以手动呈现指定字段 <button type="submit" class="btn btn-primary">Submit</button> </form>
|
crispy
插件还提供一些helper帮助在定义form的时候提供更多的自定义配置,例如:
1 2 3 4 5 6 7 8 9 10
| class MyForm(forms.Form): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.helper = FormHelper() self.helper.form_id = 'id-exampleForm' self.helper.form_class = 'blueForms' self.helper.form_method = 'post' self.helper.form_action = 'submit_survey'
self.helper.add_input(Submit('submit', 'Submit'))
|
crispy Layouts
可以自定义字段的更多样式,例如autocomplete、定义data、id、text input的前缀后缀、Tab字段分类等,具体文档见Layouts