django(2)、Forms

HTML forms

  • 表单提交,页面与web服务器交互数据最重要的方式.
  • 重要参数:
    • action:要请求的url
    • method:请求的方式,默认是GET方式
  • 基础信息详见:http://www.cnblogs.com/fqh202/p/8483862.html

Forms in Django

Django handles three distinct parts of the work involved in forms:

  • 1、preparing and restructuring data to make it ready for rendering,后端准备数据并渲染到前端的form;
  • 2、creating HTML forms for the data,前端创建form;
  • 3、receiving and processing submitted forms and data from the client,后端处理客户端发送过来的表单数据。

创建form类

  • 1、可以直接在app目录下新建forms.py文件;
  • 2、定义form类以及input字段:

    from django.forms import Form
    from django.forms import widgets
    from django.forms import fields
    
    
    class MyForm(Form):
        username = fields.CharField(
            # 可以对输入的数据进行初步筛选
            max_length=6,
            min_length=2,
            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(
            min_length=6,
            widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
        )

构建视图逻辑

from .forms import LoginForm


def my_login(request):
    if request.method == "GET":
        # step1: 将form实例化对象返回到前端渲染出form标签
        form_obj = LoginForm()

    elif request.method == "POST":
        # step2.1:处理前端表单以POST方式提交的数据
        form_obj = LoginForm(request.POST, request.FILES)  # 将数据作为参数传入实例
        # step3:生成错误dict,随后产生的错误信息都会以字段名为key,以设定的错误信息为value存储到dict中,并返回到前端
        errors = form_obj.errors
        # step4:验证数据是否符合字段设定的规则
        if form_obj.is_valid():
            # step5:若数据符合form中设定的规则
            values = form_obj.cleaned_data  # 转换成dict格式
            print('values',values)
            # dict格式:values {'username': 'alex', 'pwd': 'abc123'}
            username = values.get('username')
            pwd = values.get('pwd')
            if username=='alex' and pwd=='abc123':
                return HttpResponse('<h1>登录成功</h1>')

            errors['invaild_cred']='用户名或密码错误'

    return render(request, 'login.html', locals())
步骤解析

1、导入自定义的form类并实例化:form_obj = MyForm()

for i in form_obj:
    print(i)
    
# 根据form中的字段生成对应的标签,name属性为form中对应的字段值
<input type="text" name="username" id="i1" class="c1" maxlength="6" minlength="2" required />
<input type="password" name="pwd" class="c1" minlength="6" required id="id_pwd" />

2、若填充数据,对象会调用方法对传入的数据进行验证,若有错误信息,则放进 errors 字典中,且is_valid()返回False

# step1
def is_valid(self):
    # 若form对象内容为空 且 没有错误信息则 返回True
    return self.is_bound and not self.errors


# step2
@property
def errors(self):
    if self._errors is None:
        self.full_clean()
        
    # 不为空就直接返回,is_valid()结果为false
    return self._errors
  
  
# step3
def full_clean(self):
    self._errors = ErrorDict()
    self.cleaned_data = {}
    self._clean_fields()


def _clean_fields(self):
    for name, field in self.fields.items():
        try:
            if isinstance(field, FileField):
                initial = self.get_initial_for_field(field, name)
                value = field.clean(value, initial)
            else:
                value = field.clean(value)
            self.cleaned_data[name] = value
            
            if hasattr(self, 'clean_%s' % name):
                value = getattr(self, 'clean_%s' % name)()
                self.cleaned_data[name] = value
        
        except ValidationError as e:
            self.add_error(name, e)

3、无错误信息则通过obj.cleaned_data取出数据;

4、错误信息以字典形式保存在obj.errors中:

# 取出错误字典对象
errors = form_obj.errors
# 自定义添加错误信息
errors['invaild_cred']='用户名或密码错误'

# 返回到前端的错误信息标签
<ul class="errorlist"><li>Ensure this value has at least 2 characters (it has 1).</li></ul>
<ul class="errorlist"><li>Ensure this value has at least 6 characters (it has 1).</li></ul>

前端生成input标签

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
    <style>
        div{
            margin-bottom: 10px;
        }
        .error{
            font-size: 12px;
            color: red;
        }
    </style>
</head>
<body>

<form method="post" novalidate>
    {# 方法1:手动生成 #}
    {#    <label for="id_username">用户名</label><input type="text" id="id_username" name="username"><br>#}
    {#    <label for="id_password">密  码</label><input type="password" id="id_password" name="password"><br>#}

    {# 方法2:根据form实例化对象自动生成 #}
    {% for input in form_obj %}
        <label>{{ input.name }}</label>{{ input }}<br>
        {{ input.errors.0 }}
    {% endfor %}

    <input type="submit" value="提交">
    {% csrf_token %}
</form>

</body>
</html>

常用控件

// radio单选按钮,值为字符串==========================
gender = forms.ChoiceField(
    choices=((1, '男性'),(2, '女性'), (3, '中性'), ), initial=1, 
    widget=widgets.RadioSelect
)

gender = forms.CharField(
    initial=1, widget=widgets.RadioSelect(choices=((1, '男性'),(2, '女性'), (3, '中性'), ))
)


// 单select,值为字符串========================================
user = fields.CharField(
     initial=2,
     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)

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

// 多选select,值为列表==========================================
user = fields.MultipleChoiceField(
     choices=((1,'上海'),(2,'北京'),),
     initial=[1,],
     widget=widgets.SelectMultiple
)
 

// 单checkbox
gender = forms.ChoiceField(initial=[2, ],choices=((1, '上海'), (2, '北京'),), widget=widgets.CheckboxInput)


// 多选checkbox,值为列表
user = fields.MultipleChoiceField(
     initial=[2, ],
     choices=((1, '上海'), (2, '北京'),),
     widget=widgets.CheckboxSelectMultiple
)

正则方式自定义验证

from django.form import Form
from django.core.validators import RegexValidator

class UserForm(Form):
    username = forms.CharField(
        validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), 
                    RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],

自定义验证函数

from django.core.exceptions import ValidationError
import re


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 UserForm(forms.Form):
    username = forms.CharField(
            validators=[mobile_validate, ],
            widget=widgets.TextInput(attrs={'class': 'form-control'}),
        )
    

局部钩子

重载内置的clean_field()方法,在form字段中定义的验证完成后,会执行clean_field()方法,此时通过cleaned_data取出值进行判断.

# 源码最关键代码段,此处便于用户自定义
if hasattr(self, 'clean_%s' % name):
    value = getattr(self, 'clean_%s' % name)()  
    self.cleaned_data[name] = value


from django import forms
from django.forms import widgets
from django.core.exceptions import ValidationError


class UserForm(Form):
    username = forms.CharField(
                widget=widgets.TextInput(attrs={'class': 'form-control'}),
            )
    
    def clean_username(self):
        # 函数名必须为 clean_field_name
        value = self.cleaned_data['username']
        if value == 'alex':
            # 此处定制验证规则
            # ...
            raise ValidationError('用户名已存在')
        return value

全局钩子

源码分析
def full_clean(self):
    self._clean_fields()  # 先调用,生成cleaned_data
    self._clean_form()  # 对cleaned_data 中的值进行进一步判断


def _clean_fields(self):
    for name, field in self.fields.items():
        try:
            if hasattr(self, 'clean_%s' % name):
                value = getattr(self, 'clean_%s' % name)()
                self.cleaned_data[name] = value
        except ValidationError as e:
            self.add_error(name, e)
            

def _clean_form(self):
    try:
        cleaned_data = self.clean()
    except ValidationError as e:
        self.add_error(None, e)


def clean(self):
    return self.cleaned_data  
# forms.py
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator,ValidationError

class MyForm(Form):

    def clean(self):
        if self.cleaned_data.get('pwd1') != self.cleaned_data.get('pwd2'):
            raise ValidationError('密码不一致')
        else:
            return self.cleaned_data
自定义过滤器取出错误信息
from django import template
register = template.Library()

@register.filter
def get_error(error_dict):
    return error_dict.get('__all__')


// 前端显示自定义错误信息
 <div id="errors">
     {% if errors %}
        {{ errors|get_error }}
     {% endif %}
 </div>

文件上传

  • form中指定传输数据处理方式enctype="multipart/form-data"
  • 后端取出文件:文件的二进制格式保存在request.FILES中,在后台直接以字典方式取出.
配置media上传文件路径
// 1. setting.py配置
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media', "fileholder_name", ...) 
    # 若存在多级目录,可以逐个写入文件夹名称即可


// 2. urls.py配置
from django.views.static import serve
from onlineLearningSys import settings
# 仅限于debug模式
urlpatterns += [
    url(r'^media/(?P<path>.*)$', serve, {
        'document_root': settings.MEDIA_ROOT,
    }),
]


// 3. models.py
# 图片是以路径字符串的形式存储在数据库的;
image = models.ImageField(upload_to="user/%Y/%m", verbose_name='图片', max_length=100, default='static/images/xgt.jpg')


// 4. html文件,类似于imageField存储的格式是: organization/2018/03/1499239092.png, 必须在加上media前缀
 <a href="org-detail-homepage.html">
    <img width="200" height="120" class="scrollLoading" data-url="/media/{{ org.org_image }}"/>
</a>
后台取出文件并利用model方式保存
# views.py 保存用户图片信息
def ***()
    img = request.FILES.get('img')
    user = UerProfile.objects.create_user(
        username=username,
        password=password,
        img= img,
    )
    user.save()
手动保存文件
file_obj = request.FILES.get('upload_file')
f1 = open(file_obj.name, "wb")

# 方法1
for i in file_obj:
    f1.write(i)
    
# 方法2
for i in file_obj.chunks():
    f1.write(i)

猜你喜欢

转载自www.cnblogs.com/fqh202/p/9440471.html