Welcome to kimi's blog

django之AJAX组件、数据序列化组件drf、分页器LIMIT、form组件、sweetalert插件

Ajax组件

AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。ajax不是一门新的技术,而是一种使用现有标准的新方法。它本身又很多版本,我们目前学习的是jQuery版本(版本无所谓,本质一样就可以)。

功能简介:异步提交 局部刷新

优点:

不重新加载整个页面的情况下,可以跟服务器交换数据并更新部分网页内容。(客户是感觉不到的),只需要用户允许JavaScript在浏览器上执行。
1.AJAX使用JavaScript技术向服务器发送异步请求;
2.AJAX请求无需刷新整个页面
3.因为服务器响应内容不再是整个页面,而是页面中的部分内容,所以AJAX性能高;
4.两个关键点:异步请求,局部刷新

常见的场景

image

搜索引擎会根据用户输入的关键字,自动提示检索关键字。其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。

这注册过程页面时没有刷新的,只是刷新页面中我们鼠标点击的局部位置,当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应。

基本语法

//2.给按钮绑定点击事件
$('#subBtn').click(function(){
    //1.先获取两个框里面的数据
    let v1Val=$('#d1').val();
    let v2Val=$('#d2').val();
    console.log(v1Val,v2Val)
    //发送ajax请求
    $.ajax({
        url:'', //后端地址,三种填写方式,与form标签得action一致
        type:'post',  //请求方式,默认也是get
        data:{'v1':viVal,'v2':v2Val}, // 发送数据
        success:function(args){
            // 后端返回结果之后自动触发args接收后端返回得数据
             $('#d3').val(args)                 }
    })
    })

使用ajax交互 那么后端返回的数据会被args接收 不再直接影响整个浏览器页面

后端
直接获取v1,v2,进行逻辑运算

Content-Type(数据编码格式)

1.urlencoded

ajax默认的编码格式、form表单默认也是
数据格式:xxx=yyy&uuu=ooo&aaa=kkk
dajango后端会自动处理到request.POST中

2.formdata

​ django后端针对普通的键值对还是处理到request.POST中,但是针对文件会处理到request.Field

3.application/json

form表单不支持 ajax可以
<script>
$('#d1').click(function)(){
    $.ajax({
        url:'',
        type:'post',
        data:JSON.stringify({'name':'kimi','age':18}),//千万要写真实的数据
        contentType:'application/json',
        success:funcion (args){
            alert(args)
        }
    })
}
</script>
   后端需要从request.body 中获取并自己处理json.loads(request.body),json自带解码功能

form表单只支持urlencoded和formdata两个数据格式,但ajax支持三种数据格式(urlencoded、formdata和application/json)。

ajax发送json格式数据

1.确保发送json对应的数据是json格式字符串
	data:JSON.stringify({})
2.修改数据编码格式(默认是urlencoded)
	contentType:'application/json'

ajax携带文件数据

<script>
	$('#d3').click(function(){
        //1.先产生一个FormData对象
        let myFormDataObj= new FormData();
        //2.往该对象中添加普通数据
        myFormDataObj.append('name','kimi');
        myFormDataObj.append('age','18');
        //3.往该对象中添加文件数据
        myFormDataObj.append('file',$('#d2')[0].files[0])
        //4.发送ajax请求
        $.ajax({
            url:'',
            type:'post',
            data:myFormDataObj,//ajax发送文件固定的两个配置
            contentType:false, //不用编码,django认识内置对象
            processData:false, //前端不做任何操作
            success:function(args){
                alert(args)
            }
        })
})
</script>

ajax补充说明

主要针对回调函数args接收到的响应数据

1.后端request.is_ajax()

​ 用于判断当前请求是否有ajax发出,有则true,否则是false

2.后端返回的三板斧都会被args接收不再影响整个浏览器页面

3.选择使用ajax做前后端交互的时候,后端一般返回的都是字典数据

user_dict = {'code':10000,'username':'大家都会阳的':'hobby':'哎呀'}

后端将字典序列化为json格式
1.use_data = json.dumps(user_dict)
2.from django.http import JsonResponse
   return JsonResponse(user_dict)
前端反序列化
1.前端正常序列化
    let userObj = JSON.parse(args);
    console.log(userObj)
    console.log(userObj.username)
2.直接打印就行,前端认识JsonResponse
    console.log(args)
    console.log(args)

​ ajax自动反序列化后端的json格式的bytes类型数据

 dataType:'json', //在ajax里面添加这个参数,无论你怎么序列化字典,都能识别出它是字典

django内置序列化组件(drf前身)

我们知道,以后前端和后端的项目分开的做,后端要想给前端发送数据,进行交互我们一般使用的json格式数据,但是目前我们学习的只有模板语法,是通过django的Queryset对象获取的,在前端页面要进行循环代码获取字段数据,就有些繁琐,为了解决上述的问题,我们可以使用django的内置序列化组件直接将queryset对象封装成字典。

'''前后端分离的项目 视图函数只需要返回json格式的数据即可'''
1.第一种方式(繁琐)
from app01 import models
from django.http import JsonResponse
def ab_ser_func(request):
    # 1.查询所有的书籍对象
    book_queryset = models.Book.objects.all()  # queryset [对象、对象]
    # 2.封装成大字典返回
    data_dict = {}
    for book_obj in book_queryset:
        temp_dict = {}
        temp_dict['pk'] = book_obj.pk
        temp_dict['title'] = book_obj.title
        temp_dict['price'] = book_obj.price
        temp_dict['info'] = book_obj.info
        data_dict[book_obj.pk] = temp_dict  # {1:{},2:{},3:{},4:{}}
    return JsonResponse(data_dict)


2.django组件自带的数据序列化组件 后续学更厉害的drf,不同于之前的序列化

def ab_ser_func(request):
    # 1.查询所有的书籍对象
    book_queryset = models.Book.objects.all()  # queryset [对象、对象]
    # 导入内置序列化模块
    from django.core import serializers
    # 调用该模块下的方法,第一个参数是你想以什么样的方式序列化你的数据
    res = serializers.serialize('json', book_queryset)
    return HttpResponse(res)

bejson是前端查看后端代码的一个工具网站,里面有格式化校验的功能。

批量操作数据

1.create与bulk_create的时间差
def ab_bk_func(request):
    # 1.往books表中插入10万条数据
    # for i in range(1, 100000):
    # models.Books.objects.create(title='第%s本书' % i)
    """直接循环插入 10s 500条左右"""
    book_obj_list = []  # 可以用列表生成式[... for i in ... if ...]     生成器表达式(... for i in ... if ...)
    for i in range(1, 100000):
        book_obj = models.Books01(title='第%s本书' % i)  # 单纯的用类名加括号产生对象
        book_obj_list.append(book_obj)
    # 批量插入数据
    models.Books01.objects.bulk_create(book_obj_list)
    """使用orm提供的批量插入操作 5s 10万条左右"""
    # 2.查询出所有的表中并展示到前端页面
    book_queryset = models.Books01.objects.all()
    return render(request, 'BkPage.html', locals())

当我们需要批量插入数据时,数据就会在同一个页面进行展示,但是这样的效果是不美观的,所以数据要分页展示的(美观)

分页器LIMIT

分页器的思路

分页器主要听处理逻辑 代码最后很简单 
推导流程
	1.queryset支持切片操作(正数)
	2.研究各个参数之间的数学关系
 		每页固定展示多少条数据、起始位置、终止位置
 	3.自定义页码参数
    	current_page = request.GET.get('page'1)
        # 如果前端没有就默认当前页为1
 	4.前端展示分页器样式
	5.总页码数问题
    	divmod方法
 	6.前端页面页码个数渲染问题
    	后端产生 前端渲染

1.get请求也是可以携带参数的,所以我们在朝后端发送查看数据的同时可以携带一个参数告诉后端我们想看第几页的数据

current_page = request.GET.get("page",1)  # 获取用户想访问的页码  如果没有 默认展示第一页
try:  # 由于后端接受到的前端数据是字符串类型所以我们这里做类型转换处理加异常捕获
  current_page = int(current_page)
except Exception as e:
  current_page = 1
# 还需要定义页面到底展示几条数据
per_page_num = 10  # 一页展示10条数据
 
# 需要对总数据进行切片操作 需要确定切片起始位置和终止位置
start_page = ? 
end_page = ?
"""
下面需要研究current_page、per_page_num、start_page、end_page四个参数之间的数据关系
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
"""

2.数据总页面获取

内置方法之divmod
divmod(100,10)
(10, 0)  # 10页
divmod(101,10)
(10, 1)  # 11页
divmod(99,10)
(9, 9)  # 10页
# 余数只要不是0就需要在第一个数字上加一

总共需要多少页数
book_queryset = models.Book.objects.all()
all_count = book_queryset.count()  # 数据总条数
all_pager, more = divmod(all_count, per_page_num)
if more:  # 有余数则总页数加一
  all_pager += 1

3.利用start_page和end_page对总数据进行切片取值再传入前端页面就能够实现分页展示

后端
book_list = models.Book.objects.all()[start_page:end_page]
return render(request,'booklist.html',locals())

前端
{% for book in book_list %}
	<p>{{ book.title }}</p>
{% endfor %}

4.由于模板语法没有range功能,但是我们需要循环产生很多分页标签,所以考虑后端生成传递给前端页面

后端

html_srt = ''
xxx=current_page
if xxx<6:
    xxx=6
for i in range(xxx - 5,xxx + 6):
    if current_page==i:
         html_str += '<li  class="active"> <a href="?page=%s">%s</a></li>'%(i,i)
    else:
    	html_str += '<li> <a href="?page=%s">%s</a></li>'%(i,i)

前端

{{ html_str}}

最终版--写好的分页器代码

一般是存在应用app01.utils.mypage文件下的,我们需要的时候导入就行

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

自定义分页器的使用

django自带分页器模块但是使用起来很麻烦 所以我们自己封装了一个

只需要掌握使用方式即可
后端
def ab_pg_func(request):
    book_queryset = models.Books01.objects.all()
    from app01.utils.mypage import Pagination  # 导入
    current_page = request.GET.get('page') # 获取当前页
    page_obj = Pagination(current_page=current_page, all_count=book_queryset.count())  # 生成对象
    page_queryset = book_queryset[page_obj.start:page_obj.end] # 切片
    return render(request, 'pgPage.html', locals())


前端

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            {% for book_obj in page_queryset %}
            <p>{{ book_obj.title }}</p>
            {% endfor %}
            {{ page_obj.page_html|safe }}
        </div>
    </div>
</div>

form组件的基本功能

日常我们注册百度时,在百度网页访问输入内容,但输入信息规范不正确就是显示一些提示信息,那是后端传入的信息。
image

针对上述的从后端获取信息我们目前有两种方式,第一种是通过ajax绑定事件来从后端获取提示信息,但是表单是整体,如果想每个输入框都要一堆逻辑,显得繁琐。针对表单,django提供了form组件。

需求1:要是想简单实现下面的功能,如何实现前端后端联动呢?

image

后端代码

def form_func(request):
    errors_dict = {'username': '', 'password': ''}
    if request.method == 'POST':
        username = request.POST.get('username')
        password = request.POST.get('password')
        if username == 'kimi':
            errors_dict['username'] = ' 用户名不能为kimi'
        if password == '123':
            errors_dict['password'] = '密码不能为123'
    return render(request, 'formPage.html', locals())

前端代码

<form action="" method="post">
    <p>username:
        <input type="text" name="username">
        <span style="color: red">{{ errors_dict.username }}</span>
    </p>
        <p>password:
        <input type="text" name="password">
            <span style="color: red">{{ errors_dict.password }}</span>

    </p>
    <input type="submit">
</form>

上述只能用前端后端不分离的项目可以用,

需求2:获取用户数据并发送给后端校验,后端返回不符合校验规则的提示信息

基于上述的需求,我们可以引入form组件,它可以自动校验数据、自动生成标签和自动展示信息,不用我们自己手写后端代码去校验数据,前端手写生成标签和展示信息。

1.自动校验数据
2.自动生成标签
3.自动展示信息

创建form组件

from django import forms  # 引入form组件

class MyForm(forms.Form):  # 继承Form类
    username = forms.CharField(min_length=3, max_length=8)  # username字段限制字符大小范围
    age = forms.IntegerField(min_value=0, max_value=200)  # 限制字符大小访问
    email = forms.EmailField()  # 必须符合邮箱格式

校验数据的功能

直接用python解释器的终端校验数据,先引入一个 from app01 import views,后面产生一个表单对象form_obj。

通过下列表单类直接填入数据,产生一个表单对象
form_obj = views.MyForm({'username':'kimi','age':18,'email':'123'})
# 1.判断数据是否全部符合要求
form_obj.is_valid()  # 邮箱不符合要求返回False
""" 所有数据合法有效会返回true,只要有一个不符合结果都是False"""
False  # 只要有一个不符合结果都是False
"""可以拿清洗过的数据合法数据和不合法数据的提示信息"""
# 2.获取符合校验条件的数据
form_obj.cleaned_data   # 符合校验的username和password自动展示
{'username': 'kimi', 'age': 18}
# 3.获取不符合校验规则的数据及原因
form_obj.errors  # 邮箱不符合,返回提示信息
{'email': ['Enter a valid email address.']}

1.只校验类中定义好的字段对应的数据 多传的根本不做任何操作
2.默认情况下类中定义好的字段都是必填的

image

forms组件渲染标签

用了form组件,前端我们不用填写表单数据,后端直接调用它会自动产生对象,

方式1

<p>forms组件渲染标签的方式1(封装程度过高 扩展性差 主要用于本地测试):</p>
    {{ form_obj.as_p }}  # 下面的渲染方式
    {{ form_obj.as_ul }}  # 列表的渲染方式
    {{ form_obj.as_table }}  # 并排的渲染方式

后端代码

from django import forms  # 引入form组件

class MyForm(forms.Form):  # 继承Form类
    username = forms.CharField(min_length=3, max_length=8) 
    username = forms.CharField(min_length=3, max_length=8 label='用户名')  # 将标签名改为中文
    age = forms.IntegerField(min_value=0, max_value=200)  
    email = forms.EmailField()  
    
def ab_forms_func(request):
    # 1.产生一个空对象
    form_obj = MyForm()
    # 2.将该对象传递给html
    return render(request,'formsPage.html',locals())

前端页面只需要写{{ form_obj.as_p }},他就会自动生成三个p标签,并且自动绑定了各自的id,不用我们手写输入三个input标签。他们是绑定在一起的并赋予了很多限制的属性。

image

方式二

<p>forms组件渲染标签的方式2(封装程度过低 扩展性高 编写麻烦)</p>
    {{ form_obj.username.label }}   # 拿到username的label标签
    {{ form_obj.username }}  # 拿到username的input标签
    {{ form_obj.age.label }}
    {{ form_obj.age }}
    {{ form_obj.email.label }}
    {{ form_obj.email }}

我们可以拿到单独的label和input标签,我们可以对两个标签进行不一样的排版方式、渲染方式。但是也是有缺点的,如果我们想拿到所以的label标签就要一个一个的拿,编写起来相比较麻烦。

image

方式三

添加字段直接在创建的form类中添加字段就可以的

<p>forms组件渲染标签的方式3(封装程度较高 扩展性高 编写简单 推荐使用)</p>

    {% for form in form_obj %}
        <p>
            {{ form.label }}
            {{ form }}
        </p>
    {% endfor %}

image

注意事项
forms组件之负责渲染获取用户数据的标签,也就意味着form标签与按钮都需要自己写
前端的校验是弱不禁风的,最终都需要后端来校验,所以我们在使用forms组件的时候可以直接取消前端帮我们的校验。

<form action="" novalidate>

​ 直接用form表单包起来前端页面的标签,书写上上述的属性,你直接点提交按钮,是没有反应的,这个时候就需要后端来进行一些数据的逻辑判读,符合业务的需求。

forms组件展示消息

后端

后端不同请求返回的forms对象一定要是相同的变量名
def ab_forms_func(request):
    # 1.产生一个空对象
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)  # request.POST可以看成是一个字典 直接传给forms类校验 字典中无论有多少键值对都没关系 只拿类中编写的
        if form_obj.is_valid():  # 校验数据是否合法
            print(form_obj.cleaned_data)
        else:
            print(form_obj.errors)
    # 2.将该对象传递给html文件
    return render(request, 'formsPage.html', locals())

前端加了novalidate,逻辑判断就只能后端判断了,如果数据校验合法就展示数据清理过后的合理数据,否则就展示错误提示信息如下图所示

image

前端

<form action="" method="post" novalidate>
    {% for form in form_obj %}
    <p>
    {{ form.label}}
    {{ form}}
    <span>{{ form.errors.0}}</span>
    </p>
{% endfor %}

<input type="submit">
</form>
form.errors会渲染很多的标签,在后面加了0(form.errors.0),前端页面就只拿文本信息

问题1:针对错误信息的提示可以修改成各国语言?

方式一:自定义内容

​ 给字段对象添加errors_messages参数

username = forms.CharField(min_length=3, max_length=8, label='用户名',
                                 error_messages={
                                   'min_length': '用户名最少三个字符',
                                   'max_length': '用户名最多八个字符',
                                   'required': '用户名不能为空'
                               } )
email=forms.EmailField(error_message={
    'required':'邮箱不能为空',
    'invalid''邮箱格式不对'  # 针对邮箱格式的
})

方式二:修改系统语言环境

from django.conf import global_settings  django内部真正的配置文件

我们可以修改成LANGUAGE_CODE='en_use' 修改成我们需要的语言环境。

form基础功能细节补充

1.request.POST也是个大字典,可以直接填入表单类做初始化,我们不用取出表单数据,在我们用form组件时,request.POST自带的多余的字典数据不会报错,也不会添加到我们自定义类中产生的对象的属性中去
2.前端页面来说,对输入框的限制,是没有很强的约束力,如果网页html源码是可以被用户修改的,我们主要是通过后端返回,可以通过form标签novalidate属性来禁用前端的约束。
3.第一次通过get请求访问,第二次点击提交访问是通过post访问,而两种请求最终返回的界面结构是一样的,所以两次产生的Form子对象的变量名要一致,方便模板语法按照同一种方式处理。
4.虽然是同一个模板,但是由于两次引用的register_obj不一样(第二次包含用户输入和错误信息),所以展示的界面也不一样。

image

第一次用get请求 拿到没有传数据的对象,前端显示就是一个让用户输入的界面,第二次用户提交用post请求,所以拿到用户数据产生一个有数据的表单对象,再次渲染就是一个包含用户输入信息和错误信息的界面

补充知识:

我们一般修改的是settings,global_settins是包含着settings,如果找不到的可以去global_settings里面查找。

form组件功能补充

forms组件校验补充

forms组件针对字段数据的校验 提供了三种类型的校验方式(可以一起使用)

1.数据字段自带参数

直接在form组件创建的时候填写一些属性进行限制
username = forms.CharField(min_length=3, max_length=8, label='用户名',
                                 error_messages={
                                   'required': '用户名不能为空'
                               } )

2.使用正则表达式 validators

from django.core.validators import RegexValidator
class MyForm(forms.From):
        phone = forms.CharField(
        validators=[
            RegexValidator(r'^[0-9]+$','请输入数字'),
            RegexValidator(r'^159[0-9]+$','数字必须以159开头的')
        ],)

3.钩子函数(Hook)

上述都只是针对某个参数进行校验的,不能进行字段数据的内容进行校验的,这就需要用到钩子函数,钩子函数就是对我们所写的字段内容获取然后进行自定义校验规则的编写。

钩子函数是基于前两步校验(is_valid())完后存放于cleanen_data的数据做校验的。钩子函数编写校验规则时,是需要一个返回值的,就是我们编写哪个校验逻辑,结果就必须返回编写校验的数据内容。

编写代码自定义校验规则
class MyForm(forms.Form):
    username = forms.CharField(min_length=3, max_length=8)
    password = forms.CharField(min_length=3, max_length=8 label='密码')
    confirm_pwd = forms.CharField(min_length=3, max_length=8,label='确认密码')
    # 钩子函数>>>:校验的最后一环 是在字段所有的校验参数之后触发

下面的钩子函数是上述的类中编写的,是针对类中校验字段数据的信息。单独写那就不是校验类中的字段数据了。

局部钩子

1.# 局部钩子:每次只校验一个字段数据       校验用户名是否已存在
# def clean_password(sele): 校验密码的
def clean_username(self):
    username = self.cleaned_data.get('username')
    if username == 'kimi':
        self.add_error('username', '用户名kimi已存在')
        return username  # 钩子函数用什么就返回什么

全局钩子

2. # 全局钩子:一次可以校验多个字段数据     校验两次密码是否一致
def clean(self):
    password = self.cleaned_data.get('password')
    confirm_pwd = self.cleaned_data.get('confirm_pwd')
    if not password == confirm_pwd:
        self.add_error('confirm_pwd', '两次密码不一致')
        return self.cleaned_data  # 钩子函数用什么就返回什么

forms组件源码刨析

image

切入口:form_obj.is_valid()

forms组件参数补充

min_length			最小字符
max_length			最大字符
min_value			最小值
max_value			最大值
label				字段注释
error_messages		错误提示
validators			正则校验器
initial				默认值
required			是否必填,false就不用填写钩子函数了
widget				控制标签的各项属性

	widget=forms.widgets.TextInput()  # type='text'
    widget=forms.widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'kimi'})  # type='password'
    自定义样式:获取的标签有样式:添加‘form-control' 
    自定义属性:'username': 'kimi'

form常用字段与插件

创建Form类时,主要设计到字段插件,字段用于对用户请求数据的验证,插件用于自动生成HTML。widget控制标签的各项属性

initial

初始值,input框里面的初始值

class MyForm(forms.Form):
    username = forms.CharField(min_length=2,max_length=12,label='用户名',initial='kimi')  # 初始值为kimi
   

error_message

重新错误信息

class MyForm(forms.Form):
username = forms.CharField(min_length=3, max_length=8, label='用户名',
                                 error_messages={
                                   'min_length': '用户名最少三个字符',
                                   'max_length': '用户名最多八个字符',
                                   'required': '用户名不能为空'
                               } )

password

class MyForm(forms.Form):
password=forms.CharField(widget=forms.widgets.PasswordInput(attrs={'class': 'form-control c1 c2', 'username': 'kimi'})  # type='password')

radioSelect

单ridio值为字符串

class MyForm(forms.Form):
      gender = forms.fields.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "其他")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )

单选Select

class MyForm(forms.Form):
        hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球"), ),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )

多选Select

class MyForm(forms.Form):
        hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "排球"), ),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )

单选checkbox

class MyForm(forms.Form):
        keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )

多选checkbox

class MyForm(forms.Form):
        hobby = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "排球"),),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )

modelform组件

学习校验性组件的目的:绝大部分是为了数据录入数据库之前的各项审核,forms组件使用的时候需要对照模型类编写代码,不够方便。

forms组件的强化版本,更好用更简单更方便

from django import forms
from app01 import models

class MyModelForm(forms.ModelForm):
    class Meta:
        model = models.UserInfo  # 直接声明对表名UserInfo校验
        fields = '__all__'  # 直接对表中所有的字段名进行校验
        labels = {
            'username':'用户名'} # 对字段进行渲染

def ab_mf_func(request):
    modelform_obj = MyModelForm()
    if request.method == 'POST':
        modelform_obj = MyModelForm(request.POST,instance=User_obj)
        if modelform_obj.is_valid():
            modelform_obj.save()  # 进行保存,相当于models.UserInfo.objects.create(...)/update(...)
        else:
            print(modelform_obj.errors)
    return render(request, 'modelFormPage.html', locals())

如果instance没有值,那么就是新增这个modelform_obj对象;如果instance=User_obj,那么就是更改信息为传进来的User_obj

前端

<form action="" method="post" novalidate>
    {% for modelform in modelform_obj %}
    <p>
    {{ modelform.label}}
    {{ modelform}}
    <span>{{ modelform.errors.0}}</span>
    </p>
{% endfor %}

<input type="submit">
</form>

Meta属性

model = models.Book  # 对应的Model中的类
fields = "__all__"  # 字段,如果是__all__,就是表示列出所有的字段
exclude = None  # 排除的字段
labels = None  # 提示信息
help_texts = None  # 帮助提示信息
widgets = None  # 自定义插件
error_messages = None  # 自定义错误信息

ModelForm的验证

与普通的Form表单验证类型类似,ModelForm表单的验证在调用is_valid() 或访问errors 属性时隐式调用。

我们可以像使用Form类一样自定义局部钩子方法和全局钩子方法来实现自定义的校验规则

如果我们不重写具体字段并设置validators属性的化,ModelForm是按照模型中字段的validators来校验的。

save()方法

每个ModelForm还具有一个save()方法。 这个方法根据表单绑定的数据创建并保存数据库对象。 ModelForm的子类可以接受现有的模型实例作为关键字参数instance;如果提供此功能,则save()将更新该实例。 如果没有提供,save() 将创建模型的一个新实例

from myapp.models import Book
from myapp.forms import BookForm

# 根据POST数据创建一个新的form对象
>>> form_obj = BookForm(request.POST)

# 创建书籍对象
new_ book = form_obj.save()

# 基于一个书籍对象创建form对象
edit_obj = Book.objects.get(id=1)
# 使用POST提交的数据更新书籍对象
form_obj = BookForm(request.POST, instance=edit_obj)
form_obj.save()

sweetalert插件

sweetalert插件是二次确认的动态框样式
官网:https://sweetalert.js.org/

如何使用查看官方文档的guides
样式很多,下面是确认删除的插件之一

1.确认删除
swal({
title: "Are you sure?",
text: "Once deleted, you will not be able to recover this imaginary file!",
icon: "warning",
buttons: true,
dangerMode: true,
})
.then((willDelete) => {
if (willDelete) {
  swal("Poof! Your imaginary file has been deleted!", {
    icon: "success",
  });
} else {
  swal("Your imaginary file is safe!");
}
});


2.try your password
swal({
content: {
  element: "input",
  attributes: {
    placeholder: "Type your password",
    type: "password",
  },
},
});


3.有关参数
$("#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")
                          }
                      }
                  })
              });
  })

下载插件

插件的CDN

<script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>

插件注意点

1.上述的样式类部分渲染的样式来自于bootstrap中,所有建议在使用上述样式时,将bootstrap的js和css也导入了,这样的情况下,页面效果就不会有任何问题
2.弹出的上述模态框中,可能字体会被图标掩盖一部分,可通过调整字体的上外边距来解决

posted @   魔女宅急便  阅读(81)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
Title
点击右上角即可分享
微信分享提示

目录导航