Django进阶(二)

Django

choice参数

choice参数
用户性别
用户学历
用户工作状态
客户来源
...

choices = (
  (1,'male'),
  (2,'female'),
  (3,'others')
)
gender = models.IntegerField(choices=choices)
"""
1.如果我存的是上面元组中数字会怎么样
2.如果我存的数字不在元组范围内又会怎样
	1.数字没有对应关系 是可以存的
"""    
from app01 import models
user_obj = models.Userinfo.objects.filter(pk=4).first()
print(user_obj.username)
print(user_obj.gender)
# 针对choices字段 如果你想要获取数字所对应的中文 你不能直接点字段
# 固定句式   数据对象.get_字段名_display()  当没有对应关系的时候 该句式获取到的还是数字
print(user_obj.get_gender_display())

实例1

record_choices = (('checked', "已签到"),
                  ('vacate', "请假"),
                  ('late', "迟到"),
                  ('noshow', "缺勤"),
                  ('leave_early', "早退"),
                 )
record = models.CharField("上课纪录", choices=record_choices, default="checked", 

实例1

score_choices = ((100, 'A+'),
                 (90, 'A'),
                 (85, 'B+'),
                 (80, 'B'),
                 (70, 'B-'),
                 (60, 'C+'),
                 (50, 'C'),
                 (40, 'C-'),
                 (0, ' D'),
                 (-1, 'N/A'),
                 (-100, 'COPY'),
                 (-1000, 'FAIL'),
                )
score = models.IntegerField("本节成绩", choices=score_choices, default=-1)

MTV与MVC模型

django号称是MTV框架,其实他还是MVC框架
MTV:
M:models ,模型,就是数据模型,负责数据的存取;
T: templates ,模板,负责页面的展示逻辑;
V: views ,视图函数,负责业务逻辑的处理;
MVC:
M:models, 模型,就是数据模型,负责数据的存取;
V: views, 视图,负责页面的展示逻辑;
C: contronner(路由匹配), 控制器,负责业务逻辑的处理;

注: 核心目的就是为了解耦,提高开发效率

ajax

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。(个人经验Asyn开头,基本都是异步相关)

AJAX 不是新的编程语言,而是一种使用现有标准的新方法。

AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)

AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。

  • 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
  • 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。

ajax语法结构

$.ajax({
  url:'',  # 指定朝哪个后端地址发送请求  不写默认朝当前地址提交
  type:'post',  # 指定提交方式
  data:{'username':'jason','password':'123'},
  success:function(data){  # data指代的就是后端返回的异步提交的结果
    # 异步回调机制逻辑处理代码
  }
})

异步提交

同步异步:描述的任务的提交方式

​ 同步:提交任务之后 原地等待任务的返回结果 期间不干任何事儿
​ 异步:提交任务之后 不愿地等待 直接执行下一行代码 任务的返回通过回调机制

阻塞非阻塞:程序的运行状态

​ 程序运行的三状态图

局部刷新

​ 一个页面 不是整体刷新 而是页面的某个地方局部刷新

Ajax是一门js的技术,基于原生js开发实现的,但是原生的js写代码过于繁琐

只学习如何使用jQuery实现ajax

基本语法结构:

$.ajax({
  url:'', # 指定抄那个后端地址发送请求,不写默认朝当前地址提交
  type:'post', # 指定提交方式
  data: {'username':'json', 'password': '123'}
  success:function(data){
    # data指代的是后端返回的异步提交的结果
    # 异步回调机制逻辑代码处理
  }
})

什么是 JSON ?

  • JSON 指的是 JavaScript 对象表示法(JavaScript Object Notation)
  • JSON 是轻量级的文本数据交换格式
  • JSON 独立于语言 *
  • JSON 具有自我描述性,更易理解
  • JSON 使用 JavaScript 语法来描述数据对象,但是 JSON 仍然独立于语言和平台。JSON 解析器和 JSON 库支持许多不同的编程语言。

合格的json对象(json只认双引的字符串格式):

["one", "two", "three"]
{ "one": 1, "two": 2, "three": 3 }
{"names": ["张三", "李四"] }
[ { "name": "张三"}, {"name": "李四"} ] 

不合格的json对象:

{ name: "张三", 'age': 32 }  // 属性名必须使用双引号
[32, 64, 128, 0xFFF] // 不能使用十六进制值
{ "name": "张三", "age": undefined }  // 不能使用undefined
{ "name": "张三",
  "birthday": new Date('Fri, 26 Aug 2011 07:13:10 GMT'),
  "getName":  function() {return this.name;}  // 不能使用函数和日期对象
}

实例:

1.展示一个前端页面 页面上有三个输入框 前两个框输入数字 点击按钮朝后端发请求
页面不刷新的情况下 完成数字的加法运算

<input type="text" id="t1"> + <input type="text" id="t2"> = <input type="text" id="t3">
<p>
<button id="b1">计算</button>
</p>

$('#b1').on('click',function () {
  // 朝后端提交post数据
  $.ajax({
    // 1.到底朝后端哪个地址发数据
    url:'',  // 专门用来控制朝后端提交数据的地址  不写默认就是朝当前地址提交
    // 2.到底发送什么请求
    type:'post',  // 专门制定ajax发送的请求方式
    // 3.发送的数据到底是什么
    data:{'t1':$('#t1').val(),'t2':$('#t2').val()},
    // 4.异步提交的任务 需要通过回调函数来处理
    success:function (data) {  // data形参指代的就是异步提交的返回结果
                             // 通过DOM操作将内容 渲染到标签内容上
                             $('#t3').val(data)
                            }
  })
})
from django.shortcuts import render,HttpResponse
def index(request):
  print(request.is_ajax())  # 判断当前请求是否是ajax请求
  if request.is_ajax():
    if request.method == 'POST':
      # print(request.POST)
      t1 = request.POST.get('t1')
      t2 = request.POST.get('t2')
      res = int(t1) + int(t2)
      return HttpResponse(res)

前后端传输数据编码格式

contentType前后端传输数据编码格式

form表单 默认的提交数据的编码格式是urlencoded
	urlencoded
		username=admin&password=123这种就是符合urlencoded数据格式

		django后端针对username=admin&password=123的urlencoded数据格式会自动解析
		将结果打包给request.POST 用户只需要从request.POST即可获取对应信息

	formdata
		django后端针对formdata格式类型数据 也会自动解析但是不会方法request.POST中而是给你放到了request.FILES中
	# form表单传输文件的时候  编码格式就必须有默认的改为formdata
	ajax  ajax默认的提交数据的编码格式也是urlencoded
	username=jason&password=123

​ 总结:django后端针对不同的编码格式数据 会有不同的处理机制以及不同的获取该数据的方法

前后端在做数据交互的时候 一定一定要表明你所发的的数据到底是什么格式

前段后交互 你不能骗人家
你的数据时什么格式 你就应该准确无误告诉别人是什么格式

Ajax传json格式数据

​ django后端针对json格式的数据 不会自动帮你解析 会直接原封不动的给你放到request.body中
​ 你可以手动处理 获取数据

1.将bytes类型转成json格式字符串
2.利用json模块json.loads反序列化出来

json_bytes = request.body
​ json_str = str(json_bytes,encoding='utf-8')
​ json_dict = json.loads(json_str)

​ 注意点: 前后端交互数据的时候 一定要做到数据个编码格式的一致性,
​ 1. 指定contentType参数
​ contentType:'application/json',

2. 要将你发送的数据 确保是json格式的
      	 	data:JSON.stringify({'username':'jason','password':'123'})
<form action="" method="post" enctype="">   # application/x-www-form-urlencoded 发普通键值, multipart/form-data 发文件,向普通兼容,反之不行。 
 
  <input type="text" name="username">
  <input type="password" name="password">
  <input type="file" name="myfile">
  <input type="submit">
</form>

ajax传文件

    需要利用内置对象 Formdata
    该对象既可以传普通的键值 也可以传文件
    
    # 获取input获取用户上传文件的文件的内容
    // 1.先通过jquery查找到该标签
    // 2.将jquery对象转换成原生的js对象
    // 3.利用原生js对象的方法 直接获取文件内容
    $('#t3')[0].files[0]

​ $('#b1').click(function () {
​ // 1.先生成一个formdata对象
​ var myFormData = new FormData();
​ // 2.朝对象中添加普通的键值
​ myFormData.append('username',$("#t1").val());
​ myFormData.append('password',$("#t2").val());
​ // 3.朝对象中添加文件数据
​ // 1.先通过jquery查找到该标签
​ // 2.将jquery对象转换成原生的js对象
​ // 3.利用原生js对象的方法 直接获取文件内容
​ myFormData.append('myfile',$('#t3')[0].files[0]);
​ $.ajax({
​ url:'',
​ type:'post',
​ data:myFormData, // 直接丢对象

        // ajax传文件 一定要指定两个关键性的参数
        contentType:false,  // 不用任何编码 因为formdata对象自带编码 django能够识别该对象
        processData:false,  // 告诉浏览器不要处理我的数据 直接发就行

        success:function (data) {
            alert(data)
        }
    })
})

“”“

ajax传文件需要注意的事项
1.利用formdata对象 能够简单的快速传输数据 (普通键值 + 文件)
2.有几个参数
data:formdata对象

contentType:false
processData:false

”“”

序列化组件

自动把数据序列化

1.将用户表的数据 查询出来 返回给前端
给前端的是一个大字典 字典里面的数据的一个个的字段
from django.core import serializers
def ser(request):
user_queryset = models.Userinfo.objects.all()
# [{},{},{},{}]
# user_list = []
# for user_obj in user_queryset:
#     user_list.append({
#         'username':user_obj.username,
#         'password':user_obj.password,
#         'gender':user_obj.get_gender_display(),
#     })
res = serializers.serialize('json',user_queryset)
print(res)
return render(request,'ser.html',locals())

ajax结合sweetalert联合

 ajax + sweetalert
        $("#b55").click(function () {
        swal({
                    title: "你确定要删除吗?",
                    text: "删除可就找不回来了哦!",
                    type: "warning",
                    showCancelButton: true,  // 是否显示取消按钮
                    confirmButtonClass: "btn-danger",  // 确认按钮的样式类
                    confirmButtonText: "删除",  // 确认按钮文本
                    cancelButtonText: "取消",  // 取消按钮文本
                    closeOnConfirm: false,  // 点击确认按钮不关闭弹框
                    showLoaderOnConfirm: true  // 显示正在删除的动画效果
                },
                function () {
                    var deleteId = 2;
                    $.ajax({
                        url: "/delete_book/",
                        type: "post",
                        data: {"id": deleteId},
                        success: function (data) {
                            if (data.code === 0) {
                                swal("删除成功!", "你可以准备跑路了!", "success");
                            } else {
                                swal("删除失败", "你可以再尝试一下!", "error")
                            }
                        }
                    })
                });
    })

批量插入数据

回顾: 列表添加元素的方式:

append : # 尾部追加

extend : # 合并

insert : #索引

关键词: bulk_create

实例:新建Django,app

urls.py

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index), # 批量导入
]

models.py

from django.db import models

# Create your models here.


class Book(models.Model):
    title = models.CharField(max_length=32)

views.py

from django.shortcuts import render
from app01 import models
# Create your views here.

# 批量插入两种方法
def index(request):
# 1.往书籍表中插入数据 1000
for i in range(1000):  # 这种插入方式 效率极低
     models.Book.objects.create(title='第%s本书'%i)
book_list = []
    book_queryest = models.Book.objects.all()
    return render(request,'index.html', locals())
 # 1.往书籍表中插入数据 100000
for i in range(100000):  # 时间还是很短  两者差距很大
  book_list.append(models.Book(title='第%s本书'%i))
  models.Book.objects.bulk_create(book_list)  # 批量插入数据
  # 2.将刚刚插入的数据查询出来展示到前端
  book_queryset = models.Book.objects.all()
  return render(request,'index.html',locals())

自定义分页器

  1. queryset : 对象支持切片取值

封装:项目下新建文件夹utils,在该文件夹下mypage.py文件,将下列代码复制进去

class Pagination(object):
  def __init__(self, current_page, all_count, per_page_num=10, pager_count=11):
    """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数

        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        获取数据用page_data而不再使用原始的queryset
        获取前端分页样式用page_obj.page_html
        """
    try:
      current_page = int(current_page)
      except Exception as e:
        current_page = 1

        if current_page < 1:
          current_page = 1

          self.current_page = current_page

          self.all_count = all_count
          self.per_page_num = per_page_num

          # 总页码
          all_pager, tmp = divmod(all_count, per_page_num)
          if tmp:
            all_pager += 1
            self.all_pager = all_pager

            self.pager_count = pager_count
            self.pager_count_half = int((pager_count - 1) / 2)

            @property
            def start(self):
              return (self.current_page - 1) * self.per_page_num

            @property
            def end(self):
              return self.current_page * self.per_page_num

            def page_html(self):
              # 如果总页码 < 11个:
              if self.all_pager <= self.pager_count:
                pager_start = 1
                pager_end = self.all_pager + 1
                # 总页码  > 11
                else:
                  # 当前页如果<=页面上最多显示11/2个页码
                  if self.current_page <= self.pager_count_half:
                    pager_start = 1
                    pager_end = self.pager_count + 1

                    # 当前页大于5
                    else:
                      # 页码翻到最后
                      if (self.current_page + self.pager_count_half) > self.all_pager:
                        pager_end = self.all_pager + 1
                        pager_start = self.all_pager - self.pager_count + 1
                        else:
                          pager_start = self.current_page - self.pager_count_half
                          pager_end = self.current_page + self.pager_count_half + 1

                          page_html_list = []
                          # 添加前面的nav和ul标签
                          page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
                          first_page = '<li><a href="?page=%s">首页</a></li>' % (1)
                          page_html_list.append(first_page)

                          if self.current_page <= 1:
                            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
                            else:
                              prev_page = '<li><a href="?page=%s">上一页</a></li>' % (self.current_page - 1,)

                              page_html_list.append(prev_page)

                              for i in range(pager_start, pager_end):
                                if i == self.current_page:
                                  temp = '<li class="active"><a href="?page=%s">%s</a></li>' % (i, i,)
                                  else:
                                    temp = '<li><a href="?page=%s">%s</a></li>' % (i, i,)
                                    page_html_list.append(temp)

                                    if self.current_page >= self.all_pager:
                                      next_page = '<li class="disabled"><a href="#">下一页</a></li>'
                                      else:
                                        next_page = '<li><a href="?page=%s">下一页</a></li>' % (self.current_page + 1,)
                                        page_html_list.append(next_page)

                                        last_page = '<li><a href="?page=%s">尾页</a></li>' % (self.all_pager,)
                                        page_html_list.append(last_page)
                                        # 尾部添加标签
                                        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
                                        return ''.join(page_html_list)

前端文件:

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
</head>
<body>
{% for book_obj in book_queryest %}
    <p>{{ book_obj.title }}</p>
{% endfor %}
<nav aria-label="Page navigation">
  <ul class="pagination">
    <li>
      <a href="#" aria-label="Previous">
        <span aria-hidden="true">&laquo;</span>
      </a>
    </li>
      {{ page_html|safe }}
{#    <li><a href="?page=1">1</a></li>#}
{#    <li><a href="?page=2">2</a></li>#}
{#    <li><a href="?page=3">3</a></li>#}
{#    <li><a href="?page=4">4</a></li>#}
{#    <li><a href="?page=5">5</a></li>#}
    <li>
      <a href="#" aria-label="Next">
        <span aria-hidden="true">&raquo;</span>
      </a>
    </li>
  </ul>
</nav>



</body>
</html>

login.html:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>

</head>
<body>
{% for book_obj in page_queryset %}
    <p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}
</body>
</html>

views.py

from django.shortcuts import render,HttpResponse,redirect
from app01 import models
# Create your views here.
from django.core.exceptions import  ValidationError
def index(request):
    # 1.往书籍表中插入数据 1000
    # for i in range(1000):  # 这种插入方式 效率极低
    #     models.Book.objects.create(title='第%s本书'%i)
    book_list = []
    for i in range(100000):
        book_list.append(models.Book(title='第%s本书'%i))
    models.Book.objects.bulk_create(book_list)  # 批量插入数据
    # 2.将刚刚插入的数据查询出来展示到前端

#     # 1.获取用户想要访问的页码数
    current_page = request.GET.get('page',1)  # 如果没有page参数 默认就展示第一页
#     # 转成整型
    current_page = int(current_page)
#     # 2.每页展示10条数据
    per_page_num = 10
#
#     # 3.定义起始位置和终止位置
    start_page = (current_page - 1) * per_page_num
    end_page = current_page * per_page_num
#
#     # 4.统计数据的总条数
    book_queryset = models.Book.objects.all()
    all_count = book_queryset.count()
#
#     # 5.求数据到底需要多少页才能展示完
    page_num, more = divmod(all_count,per_page_num)  # divmod(100,10)
    if more:
        page_num += 1
    # page_num就觉得了 需要多少个页码
    page_html = ''
    xxx = current_page  # xxx就是用户点击的数字
    if current_page < 6:
         current_page = 6
    for i in range(current_page-5,current_page+6):
        if xxx == i:
            page_html += '<li class="active"><a href="?page=%s">%s</a></li>'%(i,i)
        else:
            page_html += '<li><a href="?page=%s">%s</a></li>' % (i, i)

    book_queryset = book_queryset[start_page:end_page]
    return render(request,'index.html',locals())

"""
分页思路
per_page_num = 10
current_page                   start_page                      end_page
    1                              0                                10
    2                              10                               20
    3                              20                               30
    4                              30                               40

per_page_num = 5
current_page                   start_page                      end_page
    1                              0                                5
    2                              5                                10
    3                              10                               15
    4                              15                               20

start_page = (current_page - 1) * per_page_num
end_page = current_page * per_page_num
"""


from app01.utils.mypage import Pagination
# 使用封装好的分页器代码
def login(request):
    book_queryset = models.Book.objects.all()
    current_page = request.GET.get('page',1)
    all_count = book_queryset.count()
    # 1.实例化产生对象
    page_obj = Pagination(current_page=current_page,all_count=all_count)
    # 2.对真实数据进行切片操作
    page_queryset = book_queryset[page_obj.start:page_obj.end]
    return render(request,'login.html',locals())

urls.py:

from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^login/', views.login),
]

自定义分页器的使用   新建一个py文件将代码直接拷贝过去
后端
from app01.utils.mypage import Pagination
# 使用封装好的分页器代码
def login(request):
  book_queryset = models.Book.objects.all()
  current_page = request.GET.get('page',1)
  all_count = book_queryset.count()
  # 1.实例化产生对象
  page_obj = Pagination(current_page=current_page,all_count=all_count)
  # 2.对真实数据进行切片操作
  page_queryset = book_queryset[page_obj.start:page_obj.end]
  return render(request,'login.html',locals())

前端

{% for book_obj in page_queryset %}
<p>{{ book_obj.title }}</p>
{% endfor %}
{{ page_obj.page_html|safe }}

创建多对多表关系的三种方式

  1. 全自动(推荐使用**)

    好处在于 django orm会自动帮你创建第三张关系表
    但是它只会帮你创建两个表的关系字段 不会再额外添加字段
    虽然方便 但是第三张表的扩展性较差 无法随意的添加额外的字段
    class Book(models.Model):
    ...
    authors = models.ManyToManyField(to='Author')
    
    class Author(models.Models):
    ...  
  1. 纯手动(不推荐)

    好处在于第三张表可以任意的添加额外的字段
    不足之处在于orm查询的时候 很多方法都不支持 查询的时候非常麻烦

class Book(models.Model):
...
class Author(models.Models):
...

class Book2Author(models.Model):
book_id = models.ForeignKey(to='Book')
author_id = models.ForeignKey(to='Author')
create_time = models.DateField(auto_now_add=True)
...

半自动(推荐使用******)

​ 手动建表 但是你会告诉orm 第三张表是你自己建的
​ orm只需要给我提供方便的查询方法
​ 第三种虽然可以使用orm查询方法
​ 但是不支持使用
​ add()
​ set()
​ remove()
​ clear()

class Book(models.Model):
	...
	authors = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book','author'))


class Author(models.Model):
	...
	books = models.ManyToManyField(to='Book', through='Book2Author', through_fields=('author', 'book'))
	
class Book2Author(models.Model):
	book = models.ForeignKey(to='Book')
	author = models.ForeignKey(to='Author')
	create_time = models.DateField(auto_now_add=True)
	...
# 1.半自动 一定要加两个额外的参数
	through='Book2Author', through_fields=('book','author')
# 2.后面字段的顺序
	由第三张表通过哪个字段查询单表 就把哪个字段放前面

"""
在设计项目的时候 一定要给自己留后路 防止后续的迭代更新 

"""

form组件

一、前提

​ 1.注册功能
​ 用户输入的用户名中 不能包含《书名》
​ 如果包含了 就提示用户 输入的内容不符合社会主义核心价值观
​ 用户输入的密码 不能小于三位
​ 如果密码少于三位 提示用户 密码太短了

​ 校验数据通常是前后端都有校验
但是前端校验可有可无
​ 后端也必须要有校验 反正一句话 前端可有不校验 后端必须校验!!!

1.搭建前端页面 >>> 渲染页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
        <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <link href="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdn.bootcss.com/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script>
</head>
<body>
<form action="" method="post">
    <p>username:
        <input type="text"name="userrname">
        <span style="...">{{ back_dic.username }}</span>>
    </p>
    <p>password:<input type="text" name="username">
        <span style="...">{{ back_dic.password }}</span>
    </p>
    <input type="submit">
</form>

</body>
</html>

2.获取前端用户提交的数据校验 >>> 校验数据

def reg(request):
    back_dic = {"username":'', "password":''}
    if request.method == 'POST':
        username = request.POST.get("username")
        password = request.POST.get('password')
        if "书名" in username:
            back_dic['username'] = '不符合社会主义核心价值观'
        if len(password) < 3:
            back_dic['password'] = '密码不能少于三位数'
    return request(request, 'reg.html', locals())

3.对数据的校验的结果 展示到前端页面给用户查看 >>> 展示错误信息
因书名含敏感字换为书名

form组件能够自动帮你完成上面的三件事
1.渲染页面
2.校验数据
3.展示错误信息

form组件的使用

  1. 给自己定义一个类

views:

class MyRegForm(forms.Field):
 username = forms.CharField(min_length=3, max_length=8)
 password = forms.CharField(min_length=3, max_length=8)
 email = forms.EmailField()  # 是什么数据类型就必须传什么数据类型

校验数据

python Console:

from app01 import views
# 1.给自定义的类传一个字典
obj = views.MyRegForm({'username':'jason','password':'12','email':'123'})
# 2.判断数据是否全部合法
obj.is_valid()  # 只有数据全部符合要求才会是True
Out[4]: False
  # 3.查看符合校验规则的数据
  obj.cleaned_data
  Out[5]: {'username': 'jason'}
    # 4.查看不符合条件的数据以及不符合的原因是什么
    obj.errors
    Out[6]: 
      {
        'password': ['Ensure this value has at least 3 characters (it has 2).'],
        'email': ['Enter a valid email address.']
      }

      # 5.校验数据的时候 默认情况下类里面所有的字段都必须传值
      obj = views.MyRegForm({'username':'jason','password':'123'})
      obj.is_valid()
      Out[12]: False
        obj.errors
        Out[13]: {'email': ['This field is required.']}
          # 6.默认情况下可以多传 但是绝对不能少传
          obj = views.MyRegForm({'username':'jason','password':'1233','email':'123@qq.com','xxx':'ooo'})
          obj.is_valid()
          Out[15]: True

渲染页面

formmm.html

1.forms组件只会帮你渲染获取用户输入(输入,选择,下拉框...)的标签  提交按钮需要你自己手动写

<p>三种渲染前端页面的方式</p>
<p>第一种渲染前端页面的方式:封装程度太高了 标签样式及参数不方便调整 可扩展性差(不推荐使用)
  {{ form_obj.as_p }}
  {{ form_obj.as_ul }}
  </p>

  <p>第二种渲染页面的方式:扩展性较高 不足之处在于 需要你手写的代码量比较多(不推荐使用)</p>
    <p>
    {{ form_obj.username.label }}{{ form_obj.username }}
    </p>
    <p>
    {{ form_obj.password.label }}{{ form_obj.password }}
    </p>
    <p>
    {{ form_obj.email.label }}{{ form_obj.email }}
    </p>

    <p>第三种渲染前端页面的方式:代码量和扩展性都很高(推荐使用)</p>
      {% for foo in form_obj %}
      <p>{{ foo.label }}{{ foo }}</p>
      {% endfor %}

      如何展示错误信息
      如何取消前端帮我们做的校验 form表单中添加一个参数即可
      <form action="" method="post" novalidate>

      展示错误信息   用对象点errors.0
      <form action="" method="post" novalidate>
      {% for foo in form_obj %}
      <p>
      {{ foo.label }}:{{ foo }}
        <span style="color: red">{{ foo.errors.0 }}</span>
        </p>
        {% endfor %}
        <input type="submit">
        </form>


        

views:

from django import forms


class MyRegForm(forms.Form):
  username = forms.CharField(min_length=3,max_length=8,label='用户名',
                             error_messages={
                               'min_length':'用户名最短三位',
                               'max_length':'用户名最长八位',
                               'required':'用户名不能为空'
                             },initial='我是初始值',required=False
                            )
  password = forms.CharField(min_length=3,max_length=8,label='密码',error_messages={
    'min_length':'密码最短三位',
    'max_length':'密码最长八位',
    'required':'密码不能为空'
  })
  email = forms.EmailField(label='邮箱',error_messages={
    'required':'邮箱不能为空',
    'invalid':'邮箱格式不正确'
  },required=False)

forms组件展示错误

forms组件钩子函数
        针对字段 你还可以做额外的校验   需要通过钩子函数
        
        局部钩子
            # 当你需要对某一个字段数据进行额外的一些列校验 你可以考虑使用钩子函数
            # 针对单个字段的  使用局部钩子
            def clean_username(self):
                username = self.cleaned_data.get('username')
                if '书名' in username:
                    # 给username字段下面提示错误信息
                    self.add_error('username','用户名不符合社会主义核心价值观')
                return username
        全局钩子
            # 针对多个字段的校验 使用全局钩子      eg:校验两次密码是否一致
            def clean(self):
                password = self.cleaned_data.get('password')
                confirm_password = self.cleaned_data.get('confirm_password')
                if not password == confirm_password:
                    self.add_error('confirm_password','两次密码不一致')
                return self.cleaned_data
        
        
            
    如何改变input框的type属性值
        widget= widgets.TextInput()
        widget=widgets.PasswordInput()
    如何让forms组件渲染出来的input框有form-control类属性
        widget= widgets.TextInput(attrs={'class':'form-control others'})  # 如果有多个类属性 空格隔开
        widget=widgets.PasswordInput(attrs={'class':'form-control others'})
        
            
    每个字段 还支持正则校验
        from django import forms
        from django.forms import Form
        from django.core.validators import RegexValidator
         
        class MyForm(Form):
            user = forms.CharField(
                validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
            )
原文地址:https://www.cnblogs.com/zfb123-/p/11766179.html