博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

第三节 Django之数据提交的方式和分页

Posted on 2017-04-12 14:22  Jasonhy  阅读(445)  评论(0)    收藏  举报

    这里说的数据提交,指的是,前端用户执行的操作,将这些信息提交到后台,比如,用户的登录,一般情况,要将用户名和密码提交到我们后台,我们再从后台数据库,进行数据的验证,进行一定的逻辑处理之后,再返回给前端。在这里,主要分为两种方式:

    ①新URL的方式:

     特点是:跳转到一个独立的页面,操作数据量比较大比较方便,如:

 

# url部分

url(r'^sign_in/', v2.sign_in),
url(r'^home/', v2.home),

# model部分

class User(models.Model):
    # 只创建简单的用户表 用户名唯一
    username = models.CharField(max_length=32,unique=True)
    pwd = models.CharField(max_length=32)
<!--Template部分-->

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>{{ user_msg }}</h1>
    <form action="/sign_in/" method="post">
        {% csrf_token %}
        <p>
            <input type="text" name="username" placeholder="请输入用户名">
        </p>
        <p>
            <input type="password" name="pwd" placeholder="请输入密码">
        </p>

        <p>
            <input type="submit">
        </p>
    </form>
</body>
</html>
# views处理部分

from django.shortcuts import render,redirect,HttpResponse
from app02 import models

def sign_in(request):
    '''
    登录
    :param request:
    :return:
    '''
    user_msg = None
    # 直接通过url提交默认时get请求
    if request.method == "GET":
        # 登录界面
        return render(request,"sign_in.html",{"user_msg":"游客"})
    else:
        # 登录界面登录之后,通过表单的形式进行提交,也就是我们说的新URL方式
        # 获取提交过来的数据
        u = request.POST.get("username")
        p = request.POST.get("pwd")

        # 查询数据库,看用户名和密码是否对应的上
        user = models.User.objects.filter(username=u).first()
        if user:
            # 如果用户存在,判断用户名是否存在
            if user.pwd == p:
                return redirect("/home/")
            else:
                return render(request,"sign_in.html",{"user_msg":"密码错误"})
        else:
            return render(request,"sign_in.html",{"user_msg":"用户不存在"})

def home(request):

    return render(request,"home.html")

 

    ②对话框方式,主要就是通过Ajax来提交的

     特点是:操作数量比较小的比较方便;可以直接操作,不用来回操作,也可以不用刷新。比如我们要删除某一条记录的时候,就可以直接使用这种方式了

    关于Ajax                                           

    首先Ajax不是一门新语言,它是异步的JavaScript和xml,也就是一种异步请求更新技术,它是用JavaScript编写,与xml的关系就是可以读取和返回xml文件。

    Ajax的核心对象就是xmlHttpRequest,xmlHttpRequest用于在后台与服务器交换数据,也就是说可以在不重新加载整个网页的情况下,对网页的某部分进行更新

    方法:

      ①open(method,url,async)

          method:请求的类型,主要是GET和POST

          url:文件在服务器上的位置

          async:true(异步)false(同步)

      ②send(string)

          string:仅用于POST请求

    属性:

      ①readyState:

          0:请求未初始化

          1:服务器连接已建立

          2:请求已经接收

          3:请求处理中

          4:请求已完成,且响应已就绪

      ②State:

          200:“ok”

          404:未找到页面

      ③responseText:获取字符串形式的响应数据

      ④responseXML:获得XML形式的响应数据

      ⑤onreadystatechange:存储函数(或函数名),每当readyState属性改变,就会调用该函数

    原理:ajax通过XmlHttpRequest对象执行操作,其中XmlHttpRequest对象是在浏览器中内置的一个对象,其运行原理就相当于创建了一个请求代理,通过代理去完成与服务器的交互,交互的过程用户不需要等待,还可以执行其它的操作,交互完成以后,代理再将交互的结果返回给用户页面

      第一步:创建xmlHttpRequest对象

var xmlhttp;

if (window.XMLHttpRequest){
    // IE7+, Firefox, Chrome, Opera, Safari创建方式
    xmlhttp = new XMLHttpRequest();
}else {
    // IE6, IE5 创建方式
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}

      第二步:设置open()方法和setRequestHeader()方法参数,将请求方式,请求目的地和请求类型设置到open方法中,如果是post请求,则需要设置setRequestHeader()参数

      第三步:发送执行,利用send方法,与服务器进行真正的交互执行

      第四步:获得执行结果,首先判断执行是否完成,然后通过js操作dom,将返回的responseText返回到页面

xmlhttp.onreadystatechange= function () {
    //判断是否发送成功,是否找到请求页面
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200){
        //操作页面元素
        document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
    }
};

    jQuery下的ajax                               

    jQuery封装了Ajax技术,通过调用ajax()函数,就可以进行网络请求了。

    回调函数

     如果要处理$.ajax()得到的数据,则需要使用回调函数,beforeSend、error、dataFilter、success、complete。

     beforeSend:在发送请求之前调用,并且传入一个XMLHttpReuqest作为参数

     error:在请求出错时调用,传入XMLHttpRequest对象,描述错误类型的字符串以及一个异常对象

     dataFilter:在请求成功之后调用,传入返回的数据以及“dataType”参数的值,并且必须返回新的数据(可能是处理过)传递给success回调函数

     success:当请求之后调用,传入返回后的数据,以及包含成功代码的字符串

     complete:当请求完成之后调用这个函数,无论成功或失败,传入XMLHttpRequest对象,以及一个包含成功或错误代码的字符串

数据类型
$.ajax()函数依赖服务器提供的信息来处理返回的数据。如果服务器报告说返回的数据是XML,那么返回的结果就可以用普通的XML方法或者jQuery的选择器来遍历。如果见得到其他类型,比如HTML,则数据就以文本形式来对待。
通过dataType选项还可以指定其他不同数据处理方式。除了单纯的XML,还可以指定 html、json、jsonp、script或者text。
其中,text和xml类型返回的数据不会经过处理。数据仅仅简单的将XMLHttpRequest的responseText或responseHTML属性传递给success回调函数,
'''注意''',我们必须确保网页服务器报告的MIME类型与我们选择的dataType所匹配。比如说,XML的话,服务器端就必须声明 text/xml 或者 application/xml 来获得一致的结果。
如果指定为html类型,任何内嵌的JavaScript都会在HTML作为一个字符串返回之前执行。类似的,指定script类型的话,也会先执行服务器端生成JavaScript,然后再把脚本作为一个文本数据返回。
如果指定为json类型,则会把获取到的数据作为一个JavaScript对象来解析,并且把构建好的对象作为结果返回。为了实现这个目的,他首先尝试使用JSON.parse()。如果浏览器不支持,则使用一个函数来构建。JSON数据是一种能很方便通过JavaScript解析的结构化数据。如果获取的数据文件存放在远程服务器上(域名不同,也就是跨域获取数据),则需要使用jsonp类型。使用这种类型的话,会创建一个查询字符串参数 callback=? ,这个参数会加在请求的URL后面。服务器端应当在JSON数据前加上回调函数名,以便完成一个有效的JSONP请求。如果要指定回调函数的参数名来取代默认的callback,可以通过设置$.ajax()的jsonp参数。
注意,JSONP是JSON格式的扩展。他要求一些服务器端的代码来检测并处理查询字符串参数。更多信息可以参阅 最初的文章。
如果指定了script或者jsonp类型,那么当从服务器接收到数据时,实际上是用了&lt;script&gt;标签而不是XMLHttpRequest对象。这种情况下,$.ajax()不再返回一个XMLHttpRequest对象,并且也不会传递事件处理函数,比如beforeSend。
发送数据到服务器
默认情况下,Ajax请求使用GET方法。如果要使用POST方法,可以设定type参数值。这个选项也会影响data选项中的内容如何发送到服务器。
data选项既可以包含一个查询字符串,比如 key1=value1&amp;key2=value2 ,也可以是一个映射,比如 {key1: 'value1', key2: 'value2'} 。如果使用了后者的形式,则数据再发送器会被转换成查询字符串。这个处理过程也可以通过设置processData选项为false来回避。如果我们希望发送一个XML对象给服务器时,这种处理可能并不合适。并且在这种情况下,我们也应当改变contentType选项的值,用其他合适的MIME类型来取代默认的 application/x-www-form-urlencoded 。
高级选项
global选项用于阻止响应注册的回调函数,比如.ajaxSend,或者ajaxError,以及类似的方法。这在有些时候很有用,比如发送的请求非常频繁且简短的时候,就可以在ajaxSend里禁用这个。更多关于这些方法的详细信息,请参阅下面的内容。
如果服务器需要HTTP认证,可以使用用户名和密码可以通过username和password选项来设置。
Ajax请求是限时的,所以错误警告被捕获并处理后,可以用来提升用户体验。请求超时这个参数通常就保留其默认值,要不就通过jQuery.ajaxSetup来全局设定,很少为特定的请求重新设置timeout选项。
默认情况下,请求总会被发出去,但浏览器有可能从他的缓存中调取数据。要禁止使用缓存的结果,可以设置cache参数为false。如果希望判断数据自从上次请求后没有更改过就报告出错的话,可以设置ifModified为true。
scriptCharset允许给&lt;script&gt;标签的请求设定一个特定的字符集,用于script或者jsonp类似的数据。当脚本和页面字符集不同时,这特别好用。
Ajax的第一个字母是asynchronous的开头字母,这意味着所有的操作都是并行的,完成的顺序没有前后关系。$.ajax()的async参数总是设置成true,这标志着在请求开始后,其他代码依然能够执行。强烈不建议把这个选项设置成false,这意味着所有的请求都不再是异步的了,这也会导致浏览器被锁死。
$.ajax函数返回他创建的XMLHttpRequest对象。通常jQuery只在内部处理并创建这个对象,但用户也可以通过xhr选项来传递一个自己创建的xhr对象。返回的对象通常已经被丢弃了,但依然提供一个底层接口来观察和操控请求。比如说,调用对象上的.abort()可以在请求完成前挂起请求。
相关说明
如果提交的数据有数组,需要加traditional:true,比如
            $.ajax({
                url:"",
                type:"POST",
                data:{'k':[1,2,3]},
                traditional:true,
                success:function(arg){

                }
            })

            注:data里面的key-value中的value不支持字典

            form表单提交时:
                var data = $("#fm").serialize() 获取要提交的所有数据
格式

    示例

<!--通过对话框+ajax来提交数据-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <style>
        * {
            margin: 0;
            padding: 0;
            background-color: white;
        }

        .outer1 {
            margin-left: 30px;
            margin-top: 30px;
            width: 100%;
            height: 1000px;
        }

        .shade {
            position: fixed;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            opacity: 0.5;
            background-color: grey;
        }

        .btn {
            background-color: red;
            color: white;
            width: 50px;
            height: 40px;
            font-size: 20px;
        }

        .hide {
            display: none;
        }

        .outer2 {
            position: absolute;
            background-color: white;
            width: 400px;
            height: 300px;
            top: 50%;
            left: 50%;
            margin-top: -150px;
            margin-left: -200px;
        }

        #inner{
            margin-top: 100px;
            margin-left: 120px;
        }

        #errorMsg{
            color: red;
            font-size: 10px;
        }
    </style>
</head>
<body>
<div class="outer1">
    <h1>首页</h1>
    <input class="btn" type="button" value="登陆" onclick="signIn()">
</div>

<!--遮罩-->
<div class="shade hide"></div>

<div class="outer2 hide">
    <form id="inner">
        {% csrf_token %}
        <p><input type="text" name="username" placeholder="请输入用户名"></p>
        <p><input type="password" name="pwd" placeholder="请输入密码" ></p>
        <div>
            <label id="errorMsg"></label>
            <input type="button" id="confirm" value="登陆"/>
            <input type="button" id="cancel" value="取消"/>
        </div>
    </form>

</div>
<script src="/static/js/jquery-3.2.0.min.js"></script>
<script>
    function signIn() {
        $(".outer2").removeClass("hide");
        $(".shade").removeClass("hide");

    }

    $(function () {
        cancelIn();
        confirmIn();
    });
    function cancelIn() {
         $("#cancel").click(function () {
             $(".outer2").addClass("hide");
             $(".shade").addClass("hide");
        });
    }

    function confirmIn() {
        $("#confirm").click(function () {
            //确认登陆
            //通过ajax来提交
            $.ajax({
                url:"/ajax_sign_in/",
                type:"POST",
                dataType:"JSON",
                data:$("#inner").serialize(),
                success:function (data) {
                    var state = data.status;
                    console.log(data);
                    if (state == 1){
                        $(".outer2").addClass("hide");
                        $(".shade").addClass("hide");
                    }else {
                        $("#errorMsg").text(data.msg);
                    }
                }
            })
        });
    }
</script>
</body>
</html>

# views部分

def ajax_sign_in(request):
    # 获取ajax提交过来的数据
    data = {"status":1,"msg":None}

    u = request.POST.get("username")
    p = request.POST.get("pwd")
    # 查询数据库,看用户名和密码是否对应的上
    user = models.User.objects.filter(username=u).first()
    if user:
        # 如果用户存在,判断用户名是否存在
        if user.pwd == p:
            return HttpResponse(json.dumps(data))
        else:
            data["status"] = 0
            data["msg"] = "密码不正确"
            return HttpResponse(json.dumps(data))
    else:
        data["status"] = 0
        data["msg"] = "用户不存在"
        return HttpResponse(json.dumps(data))


def home(request):
    return render(request,"home.html")
# urls部分

url(r'^home/', v2.home),
url(r'^ajax_sign_in/', v2.ajax_sign_in),
效果图:

    分页                           

<!-- pager页 -->
{% if page.has_previous %}
    <a href="/inner_pager?p={{ page.previous_page_number }}">上一页</a>
{% endif %}

{% if page.has_next %}
    <a href="/inner_pager?p={{ page.next_page_number }}">下一页</a>
{% endif %}

<!--当前页和总页数-->
<span>{{ page.number }}/{{ page.paginator.num_pages }}</span>

<!inner_pager页-->
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>内置分页</h1>

    <ul>
        {% for p in page %}
            <li>{{ p }}</li>
        {% endfor %}

    </ul>

    {% include 'pager.html'%}
</body>
</html>

# views下内置分页逻辑处理

from django.core.paginator import Paginator,EmptyPage,PageNotAnInteger
L = []
for i in range(999):
    L.append("测试数据%s" % i)

def inner_pager(request):
    '''内置分页'''
    # per_page  每页显示条目数列
    # count 数据总个数
    # num_pagers 总页数
    # page_range 总页数索引范围
    # page page对象(封装了是否有上一页和下一页)
    paginator = Paginator(L,10)
    # 获取当前页
    p = request.GET.get("p")
    if p:
        curent_page = int(p)
    else:
        curent_page = 1

    try:

        # 获取page对象
        page = paginator.page(curent_page)
        # has_next 是否有上一页
        # next_page_number 下一页页码
        # has_previous 是否有上一页
        # previous_page_number 上一页页码
        # object_list 分页之后的数据列表
        # number当前页
        # paginator paginator对象
    except PageNotAnInteger:
        page = paginator.page(1)
    except EmptyPage:
        page = paginator.page(paginator.num_pages)

    return render(request,"inner_pager.html",{"page":page})
内置分页效果图


分页扩展
# 通过自定义Paginator对象来实现
class CustomPaginatior(Paginator):
    def __init__(self,current_page,per_pager_num,*args,**kwargs):
        '''
        :param current_page: 当前页
        :param per_pager_num: 最多显示的页面数量
        :param agrs:
        :param kwagrs:
        '''
        self.current_page = int(current_page)
        self.per_pager_num = int(per_pager_num)

        super(CustomPaginatior,self).__init__(*args,**kwargs)

    def page_num_range(self):
        # 定义当前页函数
        # self.current_page 当前页
        # self.per_pager_num 最多显示的页码数
        # self.num_pages 总页数

        # 总页数小于最大显示的页数
        if self.num_pages < self.per_pager_num:
            return range(1,self.num_pages+1)
        # 总页数特别多,需要根据当前页来进行判断
        part = int(self.per_pager_num / 2)
        if self.current_page <= part:
            return range(1,self.per_pager_num+1)
        # 极值判断
        if (self.current_page + part) > self.num_pages:
            return range(self.num_pages-self.per_pager_num+1,self.num_pages + 1)
        return range(self.current_page - part,self.current_page+part+1)

pager页面进行修改:

{% if page.has_previous %}
    <a href="/inner_pager?p={{ page.previous_page_number }}">上一页</a>
{% endif %}

<!--分页扩展-->
{% for i in page.paginator.page_num_range %}
    {% if i == page.number %}
        <a class="P" href="/inner_pager?p={{ i }}">{{ i }}</a>
        {% else %}
        <a href="/inner_pager?p={{ i }}">{{ i }}</a>
    {% endif %}
{% endfor %}

{% if page.has_next %}
    <a href="/inner_pager?p={{ page.next_page_number }}">下一页</a>
{% endif %}

<!--当前页和总页数-->
<span>{{ page.number }}/{{ page.paginator.num_pages }}</span>

inner_pager函数里边,修改:

  paginator = Paginator(L,10) 为 paginator = CustomPaginatior(curent_page,11,L,10),其它不变

效果图:
自定义分页
# 自定义分页
class Pagination:
    def __init__(self,totalCount,currentPage,perPageItemNum=10,maxPageNum=7):
        # 数据总个数
        self.total_count = totalCount
        # 当前页
        try:
            v = int(currentPage)
            # 如果当前页小于0,让当前页等于1
            if v <= 0:
                v = 1
            self.current_page = v
        except Exception as e:
            self.current_page = 1

        # 每页显示条数
        self.per_page_item_num = perPageItemNum
        # 最多显示的页数
        self.max_page_num = maxPageNum

    def start(self):
        '''
        切片开始页
        :return:
        '''
        return (self.current_page-1)*self.per_page_item_num

    def end(self):
        '''
        切片结束页
        :return:
        '''
        return (self.current_page + 1) * self.per_page_item_num

    @property
    def num_pages(self):
        '''
        总页数
        :return:
        '''
        a,b = divmod(self.total_count,self.per_page_item_num)
        if b == 0:
            return a
        return a + 1

    def page_num_range(self):
        '''
        显示页码的范围
        :return:
        '''
        # 总页数小于最多显示的页数
        if self.num_pages < self.max_page_num:
            return range(1,self.num_pages + 1)

        # 总页数特别多的情况
        part =int(self.max_page_num / 2)
        if self.current_page <= part:
            return range(1,self.max_page_num + 1)

        # 极值判断
        if (self.current_page + part) > self.num_pages:
            return range(self.num_pages -self.max_page_num + 1,self.num_pages + 1)

        return range(self.current_page - part,self.current_page+part+1)

    def page_str(self):
        '''
        定义显示的页面界面
        :return:
        '''
        page_list = []
        first = "<li><a href='/my_pager?p=1'>首页</a></li>"
        page_list.append(first)

        if self.current_page == 1:
            prev = "<li><a href='#'>上一页</a>"
        else:
            prev = "<li><a href='/my_pager?p=%s'>上一页</a></li>" % (self.current_page - 1,)
        page_list.append(prev)

        for i in self.page_num_range():
            if i == self.current_page:
                temp = "<li ><a class='P' href='/my_pager?p=%s'>%s</a></li>" % (i, i)
            else:
                temp = "<li><a href='/my_pager?p=%s'>%s</a></li>" % (i, i)
            page_list.append(temp)

        if self.current_page == self.num_pages:
            nex = "<li><a href='#'>下一页</a>"
        else:
            nex = "<li><a href='/my_pager?p=%s'>下一页</a></li>" % (self.current_page + 1,)
        page_list.append(nex)

        last = "<li><a href='/my_pager?p=%s'>尾页</a></li>" % (self.num_pages,)
        page_list.append(last)

        return "".join(page_list)

# Pagination的使用
from app02 import pager
def my_pager(request):
    '''
    自定义分页
    :param request:
    :return:
    '''
    # 获取当前页
    p = request.GET.get("p")
    if p:
        curent_page = int(p)
    else:
        curent_page = 1

    page_obj = pager.Pagination(999,curent_page)

    data_list = L[page_obj.start():page_obj.end()]
    return render(request,"my_pager.html",{"data":data_list,"page_obj":page_obj})