Django之Form组件详解、图片上传及定制

Django的Form功能

●生成HTML标签

●验证用户数据(显示错误信息)

●HTML Form提交保留上次提交数据

●初始化页面显示内容

Django form 流程

1、创建类,继承form.Form

2、页面根据类的对象自动创建html标签

3、提交,request.POST

      封装到类的对象里,obj=UserInfo(request.POST)

4、用户输入是否合法

    obj.is_valid()

5、全部合法,获取全部内容

    obj.clean()

6、只有有不合法

    obj.errors

1、创建Form类

fromdjango.formsimportForm

fromdjango.formsimportwidgets

fromdjango.formsimportfields

classMyForm(Form):

    user = fields.CharField(

widget=widgets.TextInput(attrs={'id':'i1','class':'c1'})

    )

    gender = fields.ChoiceField(

choices=((1,'男'), (2,'女'),),

initial=2,

        widget=widgets.RadioSelect

    )

    city = fields.CharField(

initial=2,

widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))

    )

    pwd = fields.CharField(

widget=widgets.PasswordInput(attrs={'class':'c1'}, render_value=True)

    )

2、View函数处理

fromdjango.shortcutsimportrender, redirect

from.formsimportMyForm

defindex(request):

ifrequest.method =="GET":

        obj = MyForm()

returnrender(request,'index.html', {'form': obj})

elifrequest.method =="POST":

        obj = MyForm(request.POST, request.FILES)

ifobj.is_valid():

            values = obj.clean()

            print(values)

else:

            errors = obj.errors

            print(errors)

returnrender(request,'index.html', {'form': obj})

else:

returnredirect('http://www.google.com')

3、生成HTML

   

{{ form.user }} {{ form.user.errors }}

   

{{ form.gender }} {{ form.gender.errors }}

   

{{ form.city }} {{ form.city.errors }}

   

{{ form.pwd }} {{ form.pwd.errors }}

   

        {% csrf_token %}       

            {{ form.xxoo.label }}

            {{ form.xxoo.id_for_label }}

            {{ form.xxoo.label_tag }}

            {{ form.xxoo.errors }}

           

{{ form.user }} {{ form.user.errors }}

           

   

Form类

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;

1、Django内置字段如下:

Field

required=True,              是否允许为空

widget=None,                HTML插件

label=None,                  用于生成Label标签或显示内容

initial=None,                初始值

help_text='',                帮助信息(在标签旁边显示)

error_messages=None,        错误信息 {'required':'不能为空','invalid':'格式错误'}

show_hidden_initial=False,  是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)

    validators=[],              自定义验证规则

localize=False,              是否支持本地化

disabled=False,              是否可以编辑

label_suffix=NoneLabel内容后缀

CharField(Field)

max_length=None,            最大长度

min_length=None,            最小长度

strip=True是否移除用户输入空白

IntegerField(Field)

max_value=None,              最大值

min_value=None,              最小值

FloatField(IntegerField)

    ...

DecimalField(IntegerField)

max_value=None,              最大值

min_value=None,              最小值

max_digits=None,            总长度

decimal_places=None,        小数位长度

BaseTemporalField(Field)

input_formats=None时间格式化

DateField(BaseTemporalField)    格式:2015-09-01

TimeField(BaseTemporalField)    格式:11:12

DateTimeField(BaseTemporalField)格式:2015-09-0111:12

DurationField(Field)            时间间隔:%d %H:%M:%S.%f

    ...

RegexField(CharField)

    regex,                      自定制正则表达式

max_length=None,            最大长度

min_length=None,            最小长度

error_message=None,        忽略,错误信息使用 error_messages={'invalid':'...'}

EmailField(CharField)     

    ...

FileField(Field)

allow_empty_file=False是否允许空文件

ImageField(FileField)          ...

    注:需要PIL模块,pip3 install Pillow

    以上两个字典使用时,需要注意两点:

- form表单中 enctype="multipart/form-data"

        - view函数中 obj = MyForm(request.POST, request.FILES)

URLField(Field)

    ...

BooleanField(Field) 

    ...

NullBooleanField(BooleanField)

    ...

ChoiceField(Field)

    ...

choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)

required=True,            是否必填

widget=None,              插件,默认select插件

label=None,                Label内容

initial=None,              初始值

help_text='',              帮助提示

ModelChoiceField(ChoiceField)

    ...                        django.forms.models.ModelChoiceField

queryset,# 查询数据库中的数据

empty_label="---------",# 默认空显示内容

to_field_name=None,# HTML中value的值对应的字段

limit_choices_to=None# ModelForm中对queryset二次筛选   

ModelMultipleChoiceField(ModelChoiceField)

    ...                        django.forms.models.ModelMultipleChoiceField   

TypedChoiceField(ChoiceField)

coerce =lambdaval: val  对选中的值进行一次转换

empty_value=''空值的默认值

MultipleChoiceField(ChoiceField)

    ...

TypedMultipleChoiceField(MultipleChoiceField)

coerce =lambdaval: val  对选中的每一个值进行一次转换

empty_value=''空值的默认值

ComboField(Field)

fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式

fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])

MultiValueField(Field)

    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用

SplitDateTimeField(MultiValueField)

input_date_formats=None,  格式列表:['%Y--%m--%d','%m%d/%Y','%m/%d/%y']

input_time_formats=None格式列表:['%H:%M:%S','%H:%M:%S.%f','%H:%M']

FilePathField(ChoiceField)    文件选项,目录下文件显示在页面中

    path,                      文件夹路径

match=None,                正则匹配

recursive=False,          递归下面的文件夹

allow_files=True,          允许文件

allow_folders=False,      允许文件夹

required=True,

widget=None,

label=None,

initial=None,

help_text=''

GenericIPAddressField

protocol='both',          both,ipv4,ipv6支持的IP格式

unpack_ipv4=False解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用

SlugField(CharField)          数字,字母,下划线,减号(连字符)

    ...

UUIDField(CharField)          uuid类型

    ...

注:UUID是根据MAC以及当前时间等创建的不重复的随机字符串

>>>importuuid

# make a UUID based on the host ID and current time

>>> uuid.uuid1()# doctest: +SKIP

UUID('a8098c1a-f86e-11da-bd1a-00112444be1e')

# make a UUID using an MD5 hash of a namespace UUID and a name

>>> uuid.uuid3(uuid.NAMESPACE_DNS,'python.org')

UUID('6fa459ea-ee8a-3ca4-894e-db77e160355e')

# make a random UUID

>>> uuid.uuid4()# doctest: +SKIP

UUID('16fd2706-8baf-433b-82eb-8c7fada847da')

# make a UUID using a SHA-1 hash of a namespace UUID and a name

>>> uuid.uuid5(uuid.NAMESPACE_DNS,'python.org')

UUID('886313e1-3b8a-5372-9b90-0c9aee199e5d')

# make a UUID from a string of hex digits (braces and hyphens ignored)

>>> x = uuid.UUID('{00010203-0405-0607-0809-0a0b0c0d0e0f}')

# convert a UUID to a string of hex digits in standard form

    >>> str(x)

'00010203-0405-0607-0809-0a0b0c0d0e0f'

# get the raw 16 bytes of the UUID

    >>> x.bytes

b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f'

# make a UUID from a 16-byte string

    >>> uuid.UUID(bytes=x.bytes)

UUID('00010203-0405-0607-0809-0a0b0c0d0e0f')

2、Django内置插件:

TextInput(Input)

NumberInput(TextInput)

EmailInput(TextInput)

URLInput(TextInput)

PasswordInput(TextInput)

HiddenInput(TextInput)

Textarea(Widget)

DateInput(DateTimeBaseInput)

DateTimeInput(DateTimeBaseInput)

TimeInput(DateTimeBaseInput)

CheckboxInput

Select

NullBooleanSelect

SelectMultiple

RadioSelect

CheckboxSelectMultiple

FileInput

ClearableFileInput

MultipleHiddenInput

SplitDateTimeWidget

SplitHiddenDateTimeWidget

SelectDateWidget

常用选择插件

# 单radio,值为字符串

# user = fields.CharField(

#    initial=2,

#    widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))

# )

# 单radio,值为字符串

# user = fields.ChoiceField(

#    choices=((1, '上海'), (2, '北京'),),

#    initial=2,

#    widget=widgets.RadioSelect

# )

# 单select,值为字符串

# user = fields.CharField(

#    initial=2,

#    widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))

# )

# 单select,值为字符串

# user = fields.ChoiceField(

#    choices=((1, '上海'), (2, '北京'),),

#    initial=2,

#    widget=widgets.Select

# )

# 多选select,值为列表

# user = fields.MultipleChoiceField(

#    choices=((1,'上海'),(2,'北京'),),

#    initial=[1,],

#    widget=widgets.SelectMultiple

# )


# 单checkbox

# user = fields.CharField(

#    widget=widgets.CheckboxInput()

# )

# 多选checkbox,值为列表

# user = fields.MultipleChoiceField(

#    initial=[2, ],

#    choices=((1, '上海'), (2, '北京'),),

#    widget=widgets.CheckboxSelectMultiple

# )

在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。

方式一:

fromdjango.formsimportForm

fromdjango.formsimportwidgets

fromdjango.formsimportfields

fromdjango.core.validatorsimportRegexValidator

classMyForm(Form):

    user = fields.ChoiceField(

# choices=((1, '上海'), (2, '北京'),),

initial=2,

        widget=widgets.Select

    )

def__init__(self, *args, **kwargs):

        super(MyForm,self).__init__(*args, **kwargs)

# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)

# 或

self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')

方式二:

使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现

fromdjangoimportforms

fromdjango.formsimportfields

fromdjango.formsimportwidgets

fromdjango.formsimportmodelsasform_model

fromdjango.core.exceptionsimportValidationError

fromdjango.core.validatorsimportRegexValidator

classFInfo(forms.Form):

    authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())

# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())

自定义验证规则

方式一:

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator

class MyForm(Form):

    user = fields.CharField(

        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],

    )

方式二:

import re

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.exceptions import ValidationError

# 自定义验证规则

def mobile_validate(value):

    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')

    if not mobile_re.match(value):

        raise ValidationError('手机号码格式错误')

class PublishForm(Form):

    title = fields.CharField(max_length=20,

                            min_length=5,

                            error_messages={'required': '标题不能为空',

                                            'min_length': '标题最少为5个字符',

                                            'max_length': '标题最多为20个字符'},

                            widget=widgets.TextInput(attrs={'class': "form-control",

                                                          'placeholder': '标题5-20个字符'}))

    # 使用自定义验证规则

    phone = fields.CharField(validators=[mobile_validate, ],

                            error_messages={'required': '手机不能为空'},

                            widget=widgets.TextInput(attrs={'class': "form-control",

                                                          'placeholder': u'手机号码'}))

    email = fields.EmailField(required=False,

                            error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},

                            widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))

方法三:自定义方法

from django import forms

    from django.forms import fields

    from django.forms import widgets

    from django.core.exceptions import ValidationError

    from django.core.validators import RegexValidator

    class FInfo(forms.Form):

        username = fields.CharField(max_length=5,

                                    validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )

        email = fields.EmailField()

        def clean_username(self):

            """

            Form中字段中定义的格式匹配完之后,执行此方法进行验证

            :return:

            """

            value = self.cleaned_data['username']

            if "666" in value:

                raise ValidationError('666已经被玩烂了...', 'invalid')

            return value

方式四:同时生成多个标签进行验证

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator

############## 自定义字段 ##############

class PhoneField(fields.MultiValueField):

    def __init__(self, *args, **kwargs):

        # Define one message for all fields.

        error_messages = {

            'incomplete': 'Enter a country calling code and a phone number.',

        }

        # Or define a different message for each field.

        f = (

            fields.CharField(

                error_messages={'incomplete': 'Enter a country calling code.'},

                validators=[

                    RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),

                ],

            ),

            fields.CharField(

                error_messages={'incomplete': 'Enter a phone number.'},

                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],

            ),

            fields.CharField(

                validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],

                required=False,

            ),

        )

        super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,

                                        **kwargs)

    def compress(self, data_list):

        """

        当用户验证都通过后,该值返回给用户

        :param data_list:

        :return:

        """

        return data_list

############## 自定义插件 ##############

class SplitPhoneWidget(widgets.MultiWidget):

    def __init__(self):

        ws = (

            widgets.TextInput(),

            widgets.TextInput(),

            widgets.TextInput(),

        )

        super(SplitPhoneWidget, self).__init__(ws)

    def decompress(self, value):

        """

        处理初始值,当初始值initial不是列表时,调用该方法

        :param value:

        :return:

        """

        if value:

            return value.split(',')

        return [None, None, None]

初始化数据

在Web应用程序中开发编写功能时,时常用到获取数据库中的数据并将值初始化在HTML中的标签上。

1、Form

from django.forms import Form

from django.forms import widgets

from django.forms import fields

from django.core.validators import RegexValidator 

class MyForm(Form):

    user = fields.CharField()

    city = fields.ChoiceField(

        choices=((1, '上海'), (2, '北京'),),

        widget=widgets.Select

    )

2、Views

from django.shortcuts import render, redirect

from .forms import MyForm

def index(request):

    if request.method == "GET":

        values = {'user': 'root', 'city': 2}

        obj = MyForm(values)

        return render(request, 'index.html', {'form': obj})

    elif request.method == "POST":

        return redirect('http://www.google.com')

    else:

        return redirect('http://www.google.com')

3、HTML

    {% csrf_token %}

{{ form.user }} {{ form.user.errors }}

{{ form.city }} {{ form.city.errors }}

基于form的django图片上传

第一种,直接上传

views

classTestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

deftestimg(request):

importos,time

ifrequest.method =='GET':

        img = TestimgForm()

returnrender(request,'backend/testimg.html',{'img':img})

else:

        obj = TestimgForm(request.POST, request.FILES)

ifobj.is_valid():

img = request.FILES.get('img')

time = time.strftime('%Y%m%d%H%M%S')

ext = os.path.splitext(img.name)[1]

file_path = os.path.join('upload/article',time+ext)

#将图片重命名

f = open(file_path,'wb')

forlineinimgs.chunks():

            f.write(line)

            f.close()

returnHttpResponse('上传成功')

第二种,写入数据库

html

    Title

    {% csrf_token %}

    {{ img }}

views

classTestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

deftestimg(request):

importos,time

ifrequest.method =='GET':

        img = TestimgForm()

returnrender(request,'backend/testimg.html',{'img':img})

else:

        obj = TestimgForm(request.POST, request.FILES)

ifobj.is_valid():

title = obj.cleaned_data['title']

img = obj.cleaned_data['img']

time = time.strftime('%Y%m%d%H%M%S')

ext = os.path.splitext(img.name)[1]

img.name = time + ext

            Imgtest.objects.create(title=title,img=img)

returnHttpResponse('上传成功')

models.py

classImgtest(models.Model):title = models.CharField(verbose_name='标题',max_length=32,)    img = models.ImageField(verbose_name='缩略图',upload_to="./article/",                            help_text="大小200*200,不超过200k",                            default="/upload/article/common.jpg")

完整代码

classTestimgForm(forms.Form):

    title = fields.CharField()

    img = fields.FileField()

deftestimg(request):

importos,time

ifrequest.method =='GET':

        img = TestimgForm()

returnrender(request,'backend/testimg.html',{'img':img})

else:

        obj = TestimgForm(request.POST, request.FILES)

ifobj.is_valid():

#(request.FILES['file'])

# title = request.FILES.get('title')

# img = request.FILES.get('img')

title = obj.cleaned_data['title']

img = obj.cleaned_data['img']

time = time.strftime('%Y%m%d%H%M%S')

ext = os.path.splitext(img.name)[1]

            img.name = time + ext

# Imgtest.objects.create(title=title,img=img)

# img是对象(文件大小,文件名称,文件内容。。。)

# file_path = os.path.join('upload/article',time+ext)

file_path = os.path.join('upload/article', img.name)

f = open(file_path,'wb')

forlineinimg.chunks():

                f.write(line)

            f.close()

returnHttpResponse('...')

Form定制化

定制错误信息

mail = forms.EmailField(error_messages={'required':u'邮箱不能为空'})

定制错误规则

mobile = forms.CharField(validators=[mobile_validate,],

                        error_messages={'required':u'手机不能为空'})

from django.shortcuts import render

from django import forms

import re

from django.core.exceptions import ValidationError

def mobile_validate(value):

    mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')

    if not mobile_re.match(value):

        raise ValidationError('手机号码格式错误')


class UserInfo(forms.Form):

    email = forms.EmailField(error_messages={'required':u'邮箱不能为空'})

    host = forms.CharField(error_messages={'required':u'主机不能为空'})

    port = forms.CharField(error_messages={'required':u'端口不能为空'})

    mobile = forms.CharField(validators=[mobile_validate,], #定义错误规则函数

      error_messages={'required':u'手机不能为空'},

      widget=forms.TextInput(attrs={'class':'form-control', #定义class

      'placeholder':u'请输入手机号'}))

   beizhu = forms.CharField(required=False, #备注允许为空

      widget=forms.Textarea(attrs={'class':'form-control', #定义为多行输入框

      'placeholder':u'请输入备注'}))

   user_type_choice = (

          (0,u'普通用户'),

       (1,u'高级用户'),

      )

   user_type = forms.IntegerField(widget=forms.widgets.Select(choices=user_type_choice,attrs={'class':'form-control'}))

保存用户输入内容

obj = AccountForm.LoginForm(request.POST)

Form表单验证以及错误信息

错误信息格式:

普通格式:

from django.shortcuts import render,HttpResponse

from web.forms import account as AccountForm


def login(request):

    obj = AccountForm.LoginForm(request.POST)

    if request.method == 'POST':

        if obj.is_valid():

            all_data = obj.clean()

        else:

            #用于Form表单提交

error = obj.errors

            #print error['username'][0]

            #print error['password'][0]

       return render(request, 'account/login.html',{'obj':obj,'error':error})


    return render(request, 'account/login.html',{'obj':obj})

 创建一个simple_tag,使其输 error['username'][0]

{% load xx %}

    

    

    

        

{{ obj.username }}

            {% error_msg error.username %}

        

        

{{ obj.password }}

            {% error_msg error.password %}

        

        

    

JSON格式

def login(request):

    obj = AccountForm.LoginForm(request.POST)

    if request.method == 'POST':

        if obj.is_valid():

            all_data = obj.clean()

        else:

            #用于Ajax

error = obj.errors.as_json()

            #print error

return HttpResponse(error)

        return render(request, 'account/login.html',{'obj':obj,'error':error})

    return render(request, 'account/login.html',{'obj':obj})

Form生成select标签

静态select

动态select

当增加数据库数据时

不会更新,除非关闭程序,重新载入才会更新

解决方法:利用面向对象类的静态字段

from django import forms

import json


class ImportForm(forms.Form):

    #静态select

HOST_TYPE_LIST = (

        (0,'物理机'),

(1,'虚拟机'),

)

    host_type = forms.IntegerField(

        widget=forms.Select(choices=HOST_TYPE_LIST)

    )

    hostname = forms.CharField()


    #动态select

    # admin_dic = ((1,'yangmv'),(1,"bob"),)

    # w_obj = open('db_admin','w')

    # w_obj.write(json.dumps(admin_dic))

    # w_obj.close()

f_obj = open('db_admin','r')

    data = f_obj.read()

    data_tuple = json.loads(data)

    admin = forms.IntegerField(

        widget=forms.Select(choices=data_tuple)

    )


    def __init__(self,*args,**kwargs):

        super(ImportForm,self).__init__(*args,**kwargs)

        tmp = open('db_admin')

        data = tmp.read()

        data_tuple = json.loads(data)

        self.fields['admin'].widget.choices = data_tuple

  再次增加数据库数据,直接刷新页面即可读取到新数据

优化后代码

from django import forms

import json


class ImportForm(forms.Form):

    admin = forms.IntegerField(widget=forms.Select())

    def __init__(self,*args,**kwargs):

        super(ImportForm,self).__init__(*args,**kwargs)

        tmp = open('db_admin')

        data = tmp.read()

        data_tuple = json.loads(data)

        self.fields['admin'].widget.choices = data_tuple

使用models获取select数据

class Select(models.Model):

    username = models.CharField(max_length=16)

    password = models.CharField(max_length=32)

    

        

{{ obj.admin }}

    

def home(request):

    obj = HomeForm.ImportForm()

    return render(request, 'home/home.html',{'obj':obj})

models.Select.objects.all().values_list('id','username')

https://www.cnblogs.com/yangmv/p/5327427.html

http://www.liujiangblog.com/course/django/152

你可能感兴趣的:(Django之Form组件详解、图片上传及定制)