1. 内容回顾

11. AJAX 2 31. AJAX是什么? 4 5前端向后端发送请求的方式 6 7 8 9前端向后端发送请求的方式: 10 111. 直接在浏览器地址栏输入URL访问 --> GET 12 132. 点击a标签跳转到指定页面 --> GET 14 153. form表单 --> GET/POST 16 174. AJAX --> GET/POST 18 19 20 212. AJAX 特点 22 231. 局部更新页面 24 252. 异步发送请求 26 27 28 293. jQuery版AJAX 30 311. 语法格式: 32 331. 一般请求 34 35$.ajax({ 36 37url: "/index/", // 往哪里发送请求 38 39type: "POST", // 请求的方法 40 41data: {name:"张曌",age:16}, // 请求的数据 42 43success:function(data){ 44 45// 请求被正常响应时自动执行的回调函数 46 47console.log(data) 48 49} 50 51}) 52 532. 提交携带文件类型的数据 54 551. var formData = new FormData(); // 生成一个FormData对象 56 572. var fileObj = $("#f1")[0].files[0] // 得到用户选中的文件对象 58 593. formData.append("f1", fileObj) // 向formData对象中添加键值对数据 60 614. $.ajax({ 62 63url: "/index/", // 往哪里发送请求 64 65type: "POST", // 请求的方法 66 67processData: false, // 不让jQuery处理我的数据 68 69contentType: false, // 不让jQuery设置请求头 70 71data: formData, 72 73success:function(data){ 74 75// 请求被正常响应时自动执行的回调函数 76 77console.log(data) 78 79} 80 81}) 82 833. AJAX处理CSRF_TOKEN 84 851. 手动把csrf_token的值取到,塞进请求的data中 86 87data: { 88 89... 90 91// 取到csrf_token的值 92 93csrfmiddlewaretoken: $("[name='csrfmiddlewaretoken']").val() 94 95} 96 972. 从cookie中取csrf_token,塞进请求头中 98 99 headers: {"X-CSRFToken": $.cookie('csrftoken')}, // 从Cookie取csrf_token,并设置ajax请求头 100 101 102 1033. 在一个单独的JS文件中,给$.ajax统一设置一下请求头 104 105// 自定义一个从cookie中获取csrftoken的方法 106 107function getCookie(name) { 108 109var cookieValue = null; 110 111if (document.cookie && document.cookie !== '') { 112 113var cookies = document.cookie.split(';'); 114 115for (var i = 0; i < cookies.length; i++) { 116 117var cookie = jQuery.trim(cookies[i]); 118 119// Does this cookie string begin with the name we want? 120 121if (cookie.substring(0, name.length + 1) === (name + '=')) { 122 123cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); 124 125break; 126 127} 128 129} 130 131} 132 133return cookieValue; 134 135} 136 137var csrftoken = getCookie('csrftoken'); 138 139 140 141function csrfSafeMethod(method) { 142 143 // these HTTP methods do not require CSRF protection 144 145 return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); 146 147} 148 149 150 151$.ajaxSetup({ 152 153 beforeSend: function (xhr, settings) { 154 155if (!csrfSafeMethod(settings.type) && !this.crossDomain) { 156 157 xhr.setRequestHeader("X-CSRFToken", csrftoken); 158 159} 160 161 } 162 163}); 164 1652. 注意事项 166 1671.如果发送的data中,数据不是简单的字符串或数字,需要用JSON.stringify()转换成JSON字符串 168 1692. JS版AJAX 170 171了解即可 172 173 174 1753. 序列化(serializer) 176 177快速把ORM对象转换成JSON格式的数据 178 1794. sweetalert弹框使用 180 181 182 1835. 作业讲解 184 185 答案看上一篇博客
View Code
1. form****组件
解决这些问题1. HTML文件自己写 --> 只能生成获取用户信息的那些inptu标签等
2. 对提交过来的数据做校验,返回错误提示信息
3. 在页面中保留用户原来填写的信息
Form****介绍
我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来。
与此同时我们在好多场景下都需要对用户的输入做校验,比如校验用户是否输入,输入的长度和格式等正不正确。如果用户输入的内容有错误就需要在页面上相应的位置显示对应的错误信息.。
Django form组件就实现了上面所述的功能。
总结一下,其实form组件的主要功能如下:
- 生成页面可用的HTML标签
- 对用户提交的数据进行校验
- 保留上次输入内容
简单form提交



2. form组件的用法
1.自定义一个form类
2. 生成一个form类的实例对象
3. 在前端页面
form_obj.as_p --> 用p标签包裹我每一个字段(提示性的文本、input标签、响应的错误提示信息)
在后端
4. 在后端
form_obj.is_valid(request.POST) --> 对数据做有效性校验
form_obj.cleaned_data --> 获取所有经过校验的数据


**实例:**views.py
先定义好一个LoginForm类。

1class LoginForm(forms.Form): 2 username = forms.CharField(min_length=8, label="用户名") 3 pwd = forms.CharField(min_length=6, label="密码") 4 5def login2(request): 6 error_msg = "" 7 form_obj = LoginForm() 8 if request.method == "POST": 9 form_obj = LoginForm(request.POST) 10 if form_obj.is_valid(): 11 username = form_obj.cleaned_data.get("username") 12 pwd = form_obj.cleaned_data.get("pwd") 13 if username == "Q1mi" and pwd == "123456": 14 return HttpResponse("OK") 15 else: 16 error_msg = "用户名或密码错误" 17 return render(request, "login2.html", {"form_obj": form_obj, "error_msg": error_msg})
View Code
login2.html

1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <title>login</title> 8 <style> 9 .error { 10 color: red; 11 } 12 </style> 13</head> 14<body> 15<form action="/login2/" method="post" novalidate> 16 {% csrf_token %} 17 <p> 18 {{ form_obj.username.label }} 19 {{ form_obj.username }} 20 <span class="error">{{ form_obj.username.errors.0 }}</span> 21 </p> 22 <p> 23 {{ form_obj.pwd.label }} 24 {{ form_obj.pwd }} 25 <span class="error">{{ form_obj.pwd.errors.0 }}</span> 26 </p> 27 <p> 28 <input type="submit"> 29 <span class="error">{{ error_msg }}</span> 30 </p> 31</form> 32</body> 33</html>
View Code
看网页效果发现 也验证了form的功能:
• 前端页面是form类的对象生成的 -->生成HTML标签功能
• 当用户名和密码输入为空或输错之后 页面都会提示 -->用户提交校验功能
• 当用户输错之后 再次输入 上次的内容还保留在input框 -->保留上次输入内容
Form****那些事儿
常用字段与插件
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
initial
初始值,input框里面的初始值。
1class LoginForm(forms.Form): 2 username = forms.CharField( 3 min_length=8, 4 label="用户名", 5 initial="张三" # 设置默认值 6 ) 7 pwd = forms.CharField(min_length=6, label="密码") 8 9
error_messages
重写错误信息。
1class LoginForm(forms.Form): 2 username = forms.CharField( 3 min_length=8, 4 label="用户名", 5 initial="张三", 6 error_messages={ 7 "required": "不能为空", 8 "invalid": "格式错误", 9 "min_length": "用户名最短8位" 10 } 11 ) 12 pwd = forms.CharField(min_length=6, label="密码") 13 14
password
1class LoginForm(forms.Form): 2 ... 3 pwd = forms.CharField( 4 min_length=6, 5 label="密码", 6 widget=forms.widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True) 7 ) 8 9
radioSelect

1单radio值为字符串 2 3 4 5class LoginForm(forms.Form): 6 username = forms.CharField( 7 min_length=8, 8 label="用户名", 9 initial="张三", 10 error_messages={ 11 "required": "不能为空", 12 "invalid": "格式错误", 13 "min_length": "用户名最短8位" 14 } 15 ) 16 pwd = forms.CharField(min_length=6, label="密码") 17 gender = forms.fields.ChoiceField( 18 choices=((1, "男"), (2, "女"), (3, "保密")), 19 label="性别", 20 initial=3, 21 widget=forms.widgets.RadioSelect 22 ) 23 24
View Code
单选****Select

1class LoginForm(forms.Form): 2 ... 3 hobby = forms.fields.ChoiceField( 4 choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), 5 label="爱好", 6 initial=3, 7 widget=forms.widgets.Select 8 ) 9 10
View Code
多选****Select
1class LoginForm(forms.Form): 2 ... 3 hobby = forms.fields.MultipleChoiceField( 4 choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ), 5 label="爱好", 6 initial=[1, 3], 7 widget=forms.widgets.SelectMultiple 8 ) 9 10


单选****checkbox
1class LoginForm(forms.Form): 2 ... 3 keep = forms.fields.ChoiceField( 4 label="是否记住密码", 5 initial="checked", 6 widget=forms.widgets.CheckboxInput 7 ) 8 9
多选****checkbox
1class LoginForm(forms.Form): 2 ... 3 hobby = forms.fields.MultipleChoiceField( 4 choices=((1, "篮球"), (2, "足球"), (3, "双色球"),), 5 label="爱好", 6 initial=[1, 3], 7 widget=forms.widgets.CheckboxSelectMultiple 8 ) 9 10
关于choice的注意事项:
在使用选择标签时,需要注意choices的选项可以从数据库中获取,但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:

1from django.forms import Form 2from django.forms import widgets 3from django.forms import fields 4 5class MyForm(Form): 6 7 user = fields.ChoiceField( 8 # choices=((1, '上海'), (2, '北京'),), 9 initial=2, 10 widget=widgets.Select 11 ) 12 13 def __init__(self, *args, **kwargs): 14 super(MyForm,self).__init__(*args, **kwargs) 15 # self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),) 16 # 或 17 self.fields['user'].widget.choices = models.Classes.objects.all().values_list('id','caption') 18 19
View Code
方式二:

1from django import forms 2from django.forms import fields 3from django.forms import models as form_model 4 5class FInfo(forms.Form): 6 authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all()) 7 # authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
View Code
创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML;
1、Django内置字段如下:

1Field 2 required=True, 是否允许为空 3 widget=None, HTML插件 4 label=None, 用于生成Label标签或显示内容 5 initial=None, 初始值 6 help_text='', 帮助信息(在标签旁边显示) 7 error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'} 8 show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直) 9 validators=[], 自定义验证规则 10 localize=False, 是否支持本地化 11 disabled=False, 是否可以编辑 12 label_suffix=None Label内容后缀 13 14 15CharField(Field) 16 max_length=None, 最大长度 17 min_length=None, 最小长度 18 strip=True 是否移除用户输入空白 19 20IntegerField(Field) 21 max_value=None, 最大值 22 min_value=None, 最小值 23 24FloatField(IntegerField) 25 ... 26 27DecimalField(IntegerField) 28 max_value=None, 最大值 29 min_value=None, 最小值 30 max_digits=None, 总长度 31 decimal_places=None, 小数位长度 32 33BaseTemporalField(Field) 34 input_formats=None 时间格式化 35 36DateField(BaseTemporalField) 格式:2015-09-01 37TimeField(BaseTemporalField) 格式:11:12 38DateTimeField(BaseTemporalField)格式:2015-09-01 11:12 39 40DurationField(Field) 时间间隔:%d %H:%M:%S.%f 41 ... 42 43RegexField(CharField) 44 regex, 自定制正则表达式 45 max_length=None, 最大长度 46 min_length=None, 最小长度 47 error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'} 48 49EmailField(CharField) 50 ... 51 52FileField(Field) 53 allow_empty_file=False 是否允许空文件 54 55ImageField(FileField) 56 ... 57 注:需要PIL模块,pip3 install Pillow 58 以上两个字典使用时,需要注意两点: 59 - form表单中 enctype="multipart/form-data" 60 - view函数中 obj = MyForm(request.POST, request.FILES) 61 62URLField(Field) 63 ... 64 65 66BooleanField(Field) 67 ... 68 69NullBooleanField(BooleanField) 70 ... 71 72ChoiceField(Field) 73 ... 74 choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),) 75 required=True, 是否必填 76 widget=None, 插件,默认select插件 77 label=None, Label内容 78 initial=None, 初始值 79 help_text='', 帮助提示 80 81 82ModelChoiceField(ChoiceField) 83 ... django.forms.models.ModelChoiceField 84 queryset, # 查询数据库中的数据 85 empty_label="---------", # 默认空显示内容 86 to_field_name=None, # HTML中value的值对应的字段 87 limit_choices_to=None # ModelForm中对queryset二次筛选 88 89ModelMultipleChoiceField(ModelChoiceField) 90 ... django.forms.models.ModelMultipleChoiceField 91 92 93 94TypedChoiceField(ChoiceField) 95 coerce = lambda val: val 对选中的值进行一次转换 96 empty_value= '' 空值的默认值 97 98MultipleChoiceField(ChoiceField) 99 ... 100 101TypedMultipleChoiceField(MultipleChoiceField) 102 coerce = lambda val: val 对选中的每一个值进行一次转换 103 empty_value= '' 空值的默认值 104 105ComboField(Field) 106 fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式 107 fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),]) 108 109MultiValueField(Field) 110 PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用 111 112SplitDateTimeField(MultiValueField) 113 input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y'] 114 input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M'] 115 116FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中 117 path, 文件夹路径 118 match=None, 正则匹配 119 recursive=False, 递归下面的文件夹 120 allow_files=True, 允许文件 121 allow_folders=False, 允许文件夹 122 required=True, 123 widget=None, 124 label=None, 125 initial=None, 126 help_text='' 127 128GenericIPAddressField 129 protocol='both', both,ipv4,ipv6支持的IP格式 130 unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用 131 132SlugField(CharField) 数字,字母,下划线,减号(连字符) 133 ... 134 135UUIDField(CharField) uuid类型
View Code
2、Django内置插件如下:

1TextInput(Input) 2NumberInput(TextInput) 3EmailInput(TextInput) 4URLInput(TextInput) 5PasswordInput(TextInput) 6HiddenInput(TextInput) 7Textarea(Widget) 8DateInput(DateTimeBaseInput) 9DateTimeInput(DateTimeBaseInput) 10TimeInput(DateTimeBaseInput) 11CheckboxInput 12Select 13NullBooleanSelect 14SelectMultiple 15RadioSelect 16CheckboxSelectMultiple 17FileInput 18ClearableFileInput 19MultipleHiddenInput 20SplitDateTimeWidget 21SplitHiddenDateTimeWidget 22SelectDateWidget 23 24
View Code
3、常用选择插件

1# 单radio,值为字符串 2# user = fields.CharField( 3# initial=2, 4# widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),)) 5# ) 6 7# 单radio,值为字符串 8# user = fields.ChoiceField( 9# choices=((1, '上海'), (2, '北京'),), 10# initial=2, 11# widget=widgets.RadioSelect 12# ) 13 14# 单select,值为字符串 15# user = fields.CharField( 16# initial=2, 17# widget=widgets.Select(choices=((1,'上海'),(2,'北京'),)) 18# ) 19 20# 单select,值为字符串 21# user = fields.ChoiceField( 22# choices=((1, '上海'), (2, '北京'),), 23# initial=2, 24# widget=widgets.Select 25# ) 26 27# 多选select,值为列表 28# user = fields.MultipleChoiceField( 29# choices=((1,'上海'),(2,'北京'),), 30# initial=[1,], 31# widget=widgets.SelectMultiple 32# ) 33 34 35# 单checkbox 36# user = fields.CharField( 37# widget=widgets.CheckboxInput() 38# ) 39 40 41# 多选checkbox,值为列表 42# user = fields.MultipleChoiceField( 43# initial=[2, ], 44# choices=((1, '上海'), (2, '北京'),), 45# widget=widgets.CheckboxSelectMultiple 46# )
View Code
4, 自定义校验的方式
1. 正则
2. 自己写函数,注册到validations
3. 钩子函数
self.errors ---> self._errors = {} --> 用来存放错误信息
self.cleaned_data = {} --> 用来存放通过校验的数据
1. 局部钩子
2. 全局钩子

校验
方式一:

1from django.forms import Form 2from django.forms import widgets 3from django.forms import fields 4from django.core.validators import RegexValidator 5 6class MyForm(Form): 7 user = fields.CharField( 8 validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')], 9 ) 10 11
View Code
方式二:

1import re 2from django.forms import Form 3from django.forms import widgets 4from django.forms import fields 5from django.core.exceptions import ValidationError 6 7 8# 自定义验证规则 9def mobile_validate(value): 10 mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$') 11 if not mobile_re.match(value): 12 raise ValidationError('手机号码格式错误') 13 14 15class PublishForm(Form): 16 17 18 title = fields.CharField(max_length=20, 19 min_length=5, 20 error_messages={'required': '标题不能为空', 21 'min_length': '标题最少为5个字符', 22 'max_length': '标题最多为20个字符'}, 23 widget=widgets.TextInput(attrs={'class': "form-control", 24 'placeholder': '标题5-20个字符'})) 25 26 27 # 使用自定义验证规则 28 phone = fields.CharField(validators=[mobile_validate, ], 29 error_messages={'required': '手机不能为空'}, 30 widget=widgets.TextInput(attrs={'class': "form-control", 31 'placeholder': u'手机号码'})) 32 33 email = fields.EmailField(required=False, 34 error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'}, 35 widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
View Code
补充进阶
应用Bootstrap样式

1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta http-equiv="x-ua-compatible" content="IE=edge"> 6 <meta name="viewport" content="width=device-width, initial-scale=1"> 7 <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css"> 8 <title>login</title> 9</head> 10<body> 11<div class="container"> 12 <div class="row"> 13 <form action="/login2/" method="post" novalidate class="form-horizontal"> 14 {% csrf_token %} 15 <div class="form-group"> 16 <label for="{{ form_obj.username.id_for_label }}" 17 class="col-md-2 control-label">{{ form_obj.username.label }}</label> 18 <div class="col-md-10"> 19 {{ form_obj.username }} 20 <span class="help-block">{{ form_obj.username.errors.0 }}</span> 21 </div> 22 </div> 23 <div class="form-group"> 24 <label for="{{ form_obj.pwd.id_for_label }}" class="col-md-2 control-label">{{ form_obj.pwd.label }}</label> 25 <div class="col-md-10"> 26 {{ form_obj.pwd }} 27 <span class="help-block">{{ form_obj.pwd.errors.0 }}</span> 28 </div> 29 </div> 30 <div class="form-group"> 31 <label class="col-md-2 control-label">{{ form_obj.gender.label }}</label> 32 <div class="col-md-10"> 33 <div class="radio"> 34 {% for radio in form_obj.gender %} 35 <label for="{{ radio.id_for_label }}"> 36 {{ radio.tag }}{{ radio.choice_label }} 37 </label> 38 {% endfor %} 39 </div> 40 </div> 41 </div> 42 <div class="form-group"> 43 <div class="col-md-offset-2 col-md-10"> 44 <button type="submit" class="btn btn-default">注册</button> 45 </div> 46 </div> 47 </form> 48 </div> 49</div> 50 51<script src="/static/jquery-3.2.1.min.js"></script> 52<script src="/static/bootstrap/js/bootstrap.min.js"></script> 53</body> 54</html>
View Code
批量添加样式
可通过重写form类的init方法来实现。

1class LoginForm(forms.Form): 2 username = forms.CharField( 3 min_length=8, 4 label="用户名", 5 initial="张三", 6 error_messages={ 7 "required": "不能为空", 8 "invalid": "格式错误", 9 "min_length": "用户名最短8位" 10 } 11 ... 12 13def __init__(self, *args, **kwargs): 14 super(LoginForm, self).__init__(*args, **kwargs) 15 for field in iter(self.fields): 16 self.fields[field].widget.attrs.update({ 17 'class': 'form-control' 18 }) 19 20
View Code