Django中Ajax请求详细介绍和使用方法以及form组件的具体介绍和使用

一、Ajax介绍

# 1 异步 Javascript 和 XML:
	-异步:跟同步是对应的   
    -javascript:通过javascript来操作,发送请求 ,到服务端
    -xml:数据交互使用xml,现在主流使用json格式
    -xml:可阅读性比较高,解析复杂,占的空间大
        <name>wu</name>
        <age>19</age>
    -json:可阅读性比较高,解析简单,占的空间小
    	{"name":"wu","age":19}
	-浏览器页面局部刷新(js的dom操作)
    
    -通过js发送http的请求(go,java,php,requset)
    
# 2 同步和异步

#3 IAAS,PAAS,SAAS

# 4 同步交互和异步交互
    同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
    异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
1 原生js写ajax请求(写起来很复杂,而且需要考虑浏览器版本)
2 jquery帮咱们封装好了一个方法 ajax,我们直接调用jquery的方法,就可以发送ajax的请求
3 后期,前后端分离了,还可以继续使用jquery的ajax, axios更主流一些
4 现在我们学的jquery的ajax方法的使用


5 需求:通过Ajax,实现前端输入两个数字,服务器做加法,返回到前端页面

6 模板
    $.ajax({
        url: '',  # 不写默认朝当前地址提交数据
        method: 'post',
        data:{'a':$("#first").val() ,'b':$("#second").val() },
        success:function (data) {
            //成功触发
        },
        error:function (error) {
            //失败,触发
        }
    })
    
    
    
 	后端代码:app01.views
    
    def add(request):
    # if request.is_ajax():
    if request.method == 'POST':
        # 取出a和b
        a = int(request.POST.get('a'))  # 原本取出来的是字符串,需要转换类型
        b = int(request.POST.get('b'))
        print(a, b)

        # HttpResponse返回什么,js中的data就是什么(字符串)
        return HttpResponse(a + b)
    
     默认情况下ajax会把{'a':$("#first").val() ,'b':$("#second").val() }数据转成
     预处理数据  a=20&b=30,放到body体中    
     编码默认用urlencoded

二、Ajax上传文件

1 http请求,body体中放文件内容,ajax本质就是发送http请求,所以它可以上传文件
2 两种上传文件的方式,form表单,ajax

3 固定模板
    var formdata=new FormData() 
    form.append('username', $('#username').val())  # 添加数据部分
    formdata.append('myfile',$("#id_file")[0].files[0])  # 添加文件部分,文件名,文件对象
    # 还可以带数据
    $.ajax({
            url:'/uploadfile/',
            method: 'post',
            //上传文件必须写这两句话
            processData:false,  # 禁止预处理数据,
            contentType:false,  # 不指定编码,如果不写contentType,默认用urlencoded
            data:formdata,      # formdata内部指定了编码,并且自行处理数据
            success:function (data) {  
                console.log(data)
            }
        })

三、Ajax提交json格式

$.ajax({
            url:'/uploajson/',  //写全,是什么样就写什么样,无法重定向
            method:'post',
            contentType: 'application/json',
            //data要是json格式字符串
            //data:'{"name":"","password":""}',
            //把字典转成json格式字符串
            //JSON.stringify(dic)
            //把json格式字符串转成对象
            //JSON.parse(data)
            data:JSON.stringify({’name‘:$("#id_name1").val(),’password‘:$("#id_password1").val()}),
            success:function (data) {
                //返回字符串类型,需要转成js的对象,字典

                //1 如果:django 返回的是HttpResponse,data是json格式字符串,需要自行转成字典
                //2 如果:django 返回的是JsonResponse,data是就是字典
                //ajax这个方法做的是,如果响应数据是json格式,自动反序列化
                console.log(typeof data)
                var res=JSON.parse(data)
                console.log(typeof res)
                console.log(res.status)
                console.log(res.msg)


            }
        })

四、django内置序列化器

1 把对象转成json格式,json.dumps实现不了,
2 django内置了一个东西,可以把对象转成json格式
from django.core import serializers
book_list = Book.objects.all()    
ret = serializers.serialize("json", book_list)  # ret就是json格式字符串


ll=[]
for book in book_list:
    ll.append({'name':book.name,'price':book.pirce})  
import json
ret=json.dumps(ll)


return HttpResponse(ret)

五、Django结合sweetalter

<script>
    $('.del').on('click',function () {
        // 先将当前标签对象存储起来
        let currentBtn = $(this);
        // 二次确认弹框
        swal({
          title: "你确定要删吗?",
          text: "你可要考虑清除哦,可能需要拎包跑路哦!",
          type: "warning",
          showCancelButton: true,
          confirmButtonClass: "btn-danger",
          confirmButtonText: "是的,老子就要删!",
          cancelButtonText: "算了,算了!",
          closeOnConfirm: false,
          closeOnCancel: false,
          showLoaderOnConfirm: true
        },
        function(isConfirm) {
          if (isConfirm) {
                // 朝后端发送ajax请求删除数据之后 再弹下面的提示框
                $.ajax({
                    {#url:'/delete/user/' + currentBtn.attr('delete_id'),  // 1 传递主键值方式1#}
                    url:'/delete/user/',  // 2 放在请求体里面
                    type:'post',
                    data:{'delete_id':currentBtn.attr('delete_id')},
                    success:function (args) {  // args = {'code':'','msg':''}
                        // 判断响应状态码 然后做不同的处理
                        if(args.code === 1000){
                            swal("删了!", args.msg, "success");
                            // 1.lowb版本 直接刷新当前页面
                            {#window.location.reload()#}
                            // 2.利用DOM操作 动态刷新
                            currentBtn.parent().parent().remove()
                        }else{
                            swal('完了','出现了位置的错误','info')
                        }
                    }

                })

          } else {
            swal("怂逼", "不要说我认识你", "error");
          }
        });
    })
</script>

六、批量插入数据和choices参数

rom app01 import models

 def index(request):
     # 批量插入数据
     # 方式一(不推荐)
      for i in range(100):
          models.Book.objects.create(name='书籍%s'%i,price=i+1) # 写了100次数据库
     
     # 方式二
      ll = []
      for i in range(100):
          book = models.Book(name='书籍%s' % i, price=i + 1)
          ll.append(book)
      # 打印原生sql
      models.Book.objects.bulk_create(ll, 10)


     return render(request, 'index.html')




"""
在设计表的时候 针对可以列举完全的可能性字段
一般都是用choices参数
"""
gender_choices = (
    (1,'male'),
    (2,'female'),
    (3,'others')
)
gender = models.IntegerField(choices=gender_choices)

# 针对具有choices参数的字段 存储数据的时候还是按照字段本身的数据类型存储没有其他的约束,但是如果你存的字段在你列举的范围内 那么可以自动获取对应关系
user_obj.gender  # 数字
user_obj.get_gender_display()  # 固定格式 get_choices参数字段名_display()
"""有对应关系就拿对应关系,没有则还是数据本身不会报错"""

七、多对多表关系的三种建立方式

# 全自动:利用orm自动帮我们创建第三张关系表
	class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author')
	class Author(models.Model):
    name = models.CharField(max_length=32)
	"""
	优点:代码不需要你写 非常的方便 还支持orm提供操作第三张关系表的方法...
	不足之处:第三张关系表的扩展性极差(没有办法额外添加字段...)
	"""
# 纯手动
	class Book(models.Model):
    	name = models.CharField(max_length=32)
    
	class Author(models.Model):
    	name = models.CharField(max_length=32)
  
  	class Book2Author(models.Model):
    	book_id = models.ForeignKey(to='Book')
    	author_id = models.ForeignKey(to='Author')
  '''
  优点:第三张表完全取决于你自己进行额外的扩展
  不足之处:需要写的代码较多,不能够再使用orm提供的简单的方法
  不建议你用该方式
  '''

# 半自动
class Book(models.Model):
    name = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author',
                                     through='Book2Author',
                                     through_fields=('book','author')
                                     )
class Author(models.Model):
    name = models.CharField(max_length=32)
    # 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')

"""
through_fields字段先后顺序
    判断的本质:
        通过第三张表查询对应的表 需要用到哪个字段就把哪个字段放前面
    你也可以简化判断
        当前表是谁 就把对应的关联字段放前面
        
        
半自动:可以使用orm的正反向查询 但是没法使用add,set,remove,clear这四个方法
"""

八、自定义分页器

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)



    
后端
 def get_book(request):
   book_list = models.Book.objects.all()
   current_page = request.GET.get("page",1)
   all_count = book_list.count()
   page_obj = Pagination(current_page=current_page,all_count=all_count,per_page_num=10)
   page_queryset = book_list[page_obj.start:page_obj.end]
   return render(request,'booklist.html',locals())


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

九、forms组件介绍和校验字段功能

1 注册功能,登录功能,前端需要校验(字段长度,邮箱是否合法。。。)

2 前端校验可以没有,后端校验是必须的,使用传统方式  if判断写的很多

3 借助于forms组件,可以快速实现字段的校验
	from django.forms import Form
    
4 禁止浏览器自动检测数据,在form中填写novalidate


1、在app目录下创建一个utils的py文件

### 1 写一个类,类里写要校验的字段
from django import forms


# Create your models here.

class Myform(forms.Form):
    name = forms.CharField(max_length=10, min_length=3, widget=forms.widgets.TextInput(attrs={'class': 'form-control'}),
                           error_messages={'max_length': '不能多余10位',
                                           'min_length': '不能小于3位',
                                           'required': '必须填写'
                                           },
                           label='用户名')
    password = forms.CharField(max_length=10, min_length=3,
                               widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}),
                               error_messages={'max_length': '不能多余10位',
                                               'min_length': '不能小于3位',
                                               'required': '必须填写'
                                               },
                               label='密码')
    re_password = forms.CharField(max_length=10, min_length=3,
                                  widget=forms.widgets.PasswordInput(attrs={'class': 'form-control'}),
                                  error_messages={'max_length': '不能多余10位',
                                                  'min_length': '不能小于3位',
                                                  'required': '必须填写'
                                                  },
                                  label='确认密码')
    email = forms.EmailField(widget=forms.widgets.EmailInput(attrs={'class': 'form-control'}),
                             error_messages={'invalid': '邮箱格式不正确',
                                             'required': '必须填写'
                                             },
                             label='邮箱')

    date = forms.DateField(required=False, widget=forms.widgets.DateInput(attrs={'class': 'form-control'}),
                               label='出生日期', error_messages={'invalid': '邮箱格式不正确',
                                                             'required': '必须填写'})

    gender = forms.ChoiceField(choices=((1, '男'), (2, '女'), (3, '保密')), initial=3, widget=forms.widgets.RadioSelect(),
                               label='性别',
                               )
    text = forms.CharField(
        widget=forms.widgets.Textarea(attrs={'class': 'form-control', 'cols': 10, 'rows': 10, 'style': 'resize: none'}),
        label='个人简介', error_messages={'required': '必须填写'}, )

    
   
### 2 视图函数中使用
def register(request):
    # 数据可以是从前端传过来的,也可以是自己后台的数据

    # 我现在有以下数据
    data={'name':'wx','email':'33333@qq.com','age':900}
    # data={'email':'33333@qq.com','age':100}
    # data={'age':100}
    # 校验数据是否合法
    # 实例化得到form对象,把要校验的数据传入
    form=myforms.MyForm(data)
    # 校验数据:form.is_valid() 返回布尔类型

    if form.is_valid():
        print('校验通过')
        # 校验通过的数据
        print(form.cleaned_data)  # 不一定是上面传入的数据
    else:
        print(form.cleaned_data)
        print('校验失败')
        # 哪个字段失败了?失败的原因是什么
        print(form.errors)
        print(type(form.errors))
        from django.forms.utils import ErrorDict
        #### 重写了__str__
        print(form.errors.as_json())
        print(form.errors.as_data())

        # form.errors.as_ul()  # 是为了渲染模板


    return HttpResponse('ok')

## 视图函数
def register(request):
    if request.method=='GET':
        form=myforms.MyForm()
        return render(request,'register.html',{'form':form})
    elif request.method=='POST':
        # 数据校验
        form=myforms.MyForm(request.POST)
        if form.is_valid():
            print('校验通过,存数据库')
        else:
            print(form.errors.as_data())
            print('校验失败,返回错误')
        return HttpResponse('ok')
    
    
 ## 模板
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<hr>
<h1>手动创建模板</h1>
<form action="" method="post">
    <p>用户名:<input type="text" name="name"></p>
    <p>邮箱:<input type="text" name="email"></p>
    <p>年龄:<input type="text" name="age"></p>
    <p><input type="submit" value="提交"></p>
</form>

<hr>
<h1>半自动渲染模板1</h1>
<form action="" method="post">
    <p>用户名:{{ form.name }}</p>
    <p>邮箱:{{ form.email }}</p>
    <p>年龄:{{ form.age }}</p>
    <p><input type="submit" value="提交"></p>
</form>

<h1>半自动渲染模板2(用的最多)</h1>
<form action="" method="post">
    <p>{{ form.name.label }}--{{ form.name }}</p>
    <p>{{ form.email.label }}---{{ form.email }}</p>
    <p>{{ form.age.label }}---{{ form.age }}</p>
    <p><input type="submit" value="提交"></p>
</form>

<h1>半自动渲染模板3(用的最多)</h1>
<form action="" method="post">
    {% for foo in form %}
       <p>{{ foo.label }} :{{ foo }}</p>
    {% endfor %}

    <p><input type="submit" value="提交"></p>
</form>

<h1>全自动(了解)</h1>
<form action="" method="post">
{#    {{ form.as_ul }}#}
    {{ form.as_p }}
{#    <table>#}
{#        {{ form.as_table }}#}
{#    </table>#}

    <p><input type="submit" value="提交"></p>
</form>
</body>
</html>

十、froms渲染错误信息

1 form对象.errors 字典,所有的错误信息
2 name对象.errors 当前字段的错误信息
3 校验数据只校验类中出现的字段 多传不影响 多传的字段直接忽略
4 校验数据 默认情况下 类里面所有的字段都必须传值

方式一:不保留上次输入的结果
## 视图函数
def register(request):
    if request.method =='GET':
        form = myforms.MyForm()
        return render(request, 'register.html',{'form':form})
    else:
        form = myforms.MyForm(request.POST)
        if form.is_valid():
            return redirect('http://www.baidu.com')
        else:
            return render(request, 'register.html',{'form':form})
## 模板
 <form action="" method="post" novalidate>  # novalidate参数,禁止浏览器自动校验
	{% for foo in form %}
	<div class="form-group">
        <label for="">{{ foo.label }}</label>
            {{ foo }}
            <span class="text-danger pull-right">{{ foo.errors.0 }}</span>
        </div>
	{% endfor %}
	<div class="text-center">
		<input type="submit" value="提交" class="btn btn-danger">
	</div>
</form>





方式二:保留上一次输入的参数
def index(request):
    form_obj = MyForm()
    if request.method == 'POST':
        form_obj = MyForm(request.POST)
        if form_obj.is_valid():
            return HttpResponse('OK')
    return render(request,'index.html',{‘form’: from_obj})

"""
1.必备的条件 get请求和post传给html页面对象变量名必须一样
2.forms组件当你的数据不合法的情况下 会保存你上次的数据 让你基于之前的结果进行修改更加的人性化
"""

十一、forms组件参数配置

from django.forms import widgets

# 定制模板中的显示样式,及配置类
widget=widgets.PasswordInput(attrs={'class': 'form-control'})

# 错误信息中文显示
error_messages={'min_length': '太短了小伙子'}


class MyForm(forms.Form):
    # 校验这个字段,最大长度是32,最小长度是3
    name = forms.CharField(required=False, max_length=32, min_length=3, label='用户名',
                           widget=widgets.TextInput(attrs={'class': 'form-control'}),
                           error_messages={'min_length': '太短了小伙子',
                                          'max_length': '太长了小伙子'
                                          })
    
    password = forms.CharField(required=False, max_length=32, min_length=3, label='密码',
                               widget=widgets.PasswordInput(attrs={'class': 'form-control'}),
                               error_messages={'min_length': '太短了小伙子''max_length': '太长了小伙子'
                                              })
    
    re_password = forms.CharField(required=False, max_length=32, min_length=3, 
                               label='确认密码',
                               widget=widgets.PasswordInput(attrs={'class':'form-control'}),
                               error_messages={'min_length': '太短了小伙子''max_length': '太长了小伙子'
                                              })
    
    email = forms.EmailField(label='邮箱', error_messages={'required': '小伙子,这个必填',
                                                        'invalid':'邮箱格式不正确',
                                                        },
                             widget=widgets.TextInput(attrs={'class': 'form-control'}))
    
    age = forms.IntegerField(max_value=200, min_value=0, label='年龄',
                             error_messages={'required': '小伙子,这个必填''min_value': '太小了,小伙子''max_value': '太大了,小伙子'},
                             widget=widgets.TextInput(attrs={'class': 'form-control'}))
    
    text = forms.CharField(label='个人简介', 
                           error_messages={'required': '小伙子,这个必填'},
                           widget=widgets.Textarea(attrs={'class': 'form-control'}))
    
    date = forms.CharField(label='出生日期', 
                           error_messages={'required': '小伙子,这个必填'},
                           widget=widgets.DateInput(attrs={'class': 'form-control'}))



label		字段名
error_messages  自定义报错信息
initial  默认值
required  控制字段是否必填
"""
1.字段没有样式
2.针对不同类型的input如何修改
	text
	password
	date
	radio
	checkbox
	...
"""
widget=forms.widgets.PasswordInput(attrs={'class':'form-control c1 c2'})
# 多个属性值的话 直接空格隔开即可

# 第一道关卡里面还支持正则校验
validators=[
            RegexValidator(r'^[0-9]+$', '请输入数字'),
            RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
        ]


"""其他类型form的渲染"""
# 单选radio
    gender = forms.ChoiceField(
        choices=((1, "男"), (2, "女"), (3, "保密")),
        label="性别",
        initial=3,
        widget=forms.widgets.RadioSelect()
    )
# 单选select
    hobby = forms.ChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球")),
        label="爱好",
        initial=3,
        widget=forms.widgets.Select()
    )
# 多选select
    hobby1 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球")),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.SelectMultiple()
    )
# 单选checkbox
    keep = forms.ChoiceField(
        label="是否记住密码",
        initial="checked",
        widget=forms.widgets.CheckboxInput()
    )
# 多选checkbox
    hobby2 = forms.MultipleChoiceField(
        choices=((1, "篮球"), (2, "足球"), (3, "双色球")),
        label="爱好",
        initial=[1, 3],
        widget=forms.widgets.CheckboxSelectMultiple()
    )



    

十二、局部钩子和全局钩子(HOOK)

"""
钩子函数在forms组件中就类似于第二道关卡,能够让我们自定义校验规则

## 局部钩子的使用
    # 1 在自定义的Form类中写 clean_字段名
    # 2 取出字段的真正值,name=self.cleaned_data.get('name')
    # 3 判断自己的规则,如果判断失败,抛出ValidationError
    # 4 如果通过,return 字段名
"""
from django.forms import ValidationError


# 局部钩子
    def clean_name(self):
        # name对应的值,如何取到?
        name = self.cleaned_data.get('name')
        if name.startswith('sb'):
            # 不让校验通过
            raise ValidationError('不能以sb开头')
            #  self.add_error('name','不能以sb开头')
        else:
            # 校验通过,返回name
            return name
# 全局钩子
    def clean(self):
        # name=self.cleaned_data.get('name')
        # print(name)
        password = self.cleaned_data.get('password')
        re_password = self.cleaned_data.get('re_password')
        if password == re_password:
            return self.cleaned_data
            # return {'lqz':"nb"}
        else:
            raise ValidationError('两次密码不一致')
            #  self.add_error('re_password','两次密码不一致')
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值