request.GET之urlencode


urlencode()的作用

urlencode()是request.GET的一種方法,那他的功能是什么,我們先來測試一下。

這里以一個crm項目的一個功能為例進行測試。

首先,在URL地址中輸入想要攜帶的參數,並發送請求給后台,如下:

 后台我們首先看一下request.GET的類型以及調用urlencode()方法

 輸出結果:

 從輸出的結果中我們能看到:request.GET是一個queryDict,關於這個queryDict我們后面詳細了解,先來看request.GET.urlencode()方法的調用結果,它將url上的參數完整的‘截取’了下來。那么這個方法的工作原理是什么呢?我們來看queryDict的源碼。

首先,導入queryDict,然后點進去

from django.http.request import QueryDict

 找到urlencode方法

 根據方法的注釋我們能知道,它是用來將查詢參數編碼成字符串后返回的方法,也就是說它request.GET中的查詢參數的一個個鍵值對,編碼成字符串然后返回。關於上圖中的example說的是關於對url上參數等於某個路徑的參數的編碼,可以看一下。

接下來我們大概的看一下源碼:

def urlencode(self, safe=None):
       """
       Returns an encoded string of all query string arguments.
       # 返回所有查詢字符串參數的編碼字符串。
       :arg safe: Used to specify characters which do not require quoting, for
           example::

               >>> q = QueryDict(mutable=True)
               >>> q['next'] = '/a&b/'
               >>> q.urlencode()
               'next=%2Fa%26b%2F'
               >>> q.urlencode(safe='/')
               'next=/a%26b/'
       """
       output = []
       if safe: # 大概看了一下force_bytes函數,發現他對safe參數進行encode編碼,且字符集為utf-8,並返回bytes類型,默認safe為False,先不管。
           safe = force_bytes(safe, self.encoding)

           def encode(k, v):
               return '%s=%s' % ((quote(k, safe), quote(v, safe)))
       else:
           def encode(k, v):# 定義了一個encode()方法,並且返回了urlencode({k:v}),這里屬於自己調用自己,但屬於定義階段暫時不調用
               return urlencode({k: v})
       # print('***', self.lists) # 我在這里打印了一下self.lists(),看看它到底是個什么東西
       # 結果:*** <bound method MultiValueDict._iterlists of <QueryDict: {'query': ['1'], 'page': ['2']}>>
       # 很明顯它是一個queryDict字典,由此說明 下面的for循環中的k是這個字典的鍵,list_是鍵對應的值(列表)
       for k, list_ in self.lists():
           # 對k進行了utf-8編碼,且返回bytes類型的k
           k = force_bytes(k, self.encoding)
           output.extend(encode(k, force_bytes(v, self.encoding)) 
                         for v in list_) 
           # 結合例子,轉化一下
           # output.extend(encode(b'query',b'1'),encode(b'page',b'2'))
           # output.extend(urlencode({'query': '1'}),urlencode({'page': '2'}))
           # 這里進行了自調用,且經過研究發現urlencode({'query': '1'})是一個生成器,具體原理,看了一會,em.....沒搞明白,
           # 但者不妨礙我們知道它的功能:對參數進行了拼接,如:output.extend(['query=1'],['page=2'])
           # 最終output列表應為:['query=1','page=2']
       return '&'.join(output)
           # 最終的返回結果應為:query=1&page=2

從上述分析中我們知道,urlencode方法主要是對查詢的參數進行了特定形式的拼接。如:query=1&page=2

既然知道知道了作用,我們來進行實戰一下

例子:CRM項目在客戶展示頁,進行分頁時一並保留查詢條件。

urls.py

from django.conf.urls import url, include
from new_crm import views

urlpatterns = [
    url(r'^customer/', views.Customers.as_view(), name='customer'),

]

視圖函數:

class Customers(View):
    def get(self, request):
        if request.path_info == reverse('major:my_customer'):
            customer_obj = Customer.objects.filter(consultant=request.user_obj) # 私戶
        else:
            customer_obj = Customer.objects.filter(consultant=None)  # 公戶
        # 搜索
        query = request.GET.get('query', '')
        # # 第一種方式
        # customer_obj = customer_obj.filter(Q(name__contains=query) | Q(qq__contains=query) | Q(qq_name__contains=query))
        # # 第二種方式
        # customer_obj = customer_obj.filter(Q(('name__contains',query),('qq__contains',query),('qq_name__contains',query)))
        # # 第三種方式
        q = self.search(['name', 'qq', 'qq_name'])
        customer_obj = customer_obj.filter(q)

        # urlencode 這里進行urlencode()


        # 分頁
        page = request.GET.get('page', 1)
        pag_obj = pagination.Pagination(page=page, page_count=customer_obj.count(), per_num=3, max_show=10)
        customer_obj = customer_obj[pag_obj.start:pag_obj.end]
        return render(request, 'customer.html', locals())

    def post(self, request):
        pk_list = request.POST.getlist('pk')
        obj = Customer.objects.filter(id__in=pk_list)
        if request.POST.get('action') == 'multi_private':
            """公戶轉私戶"""
            obj.update(consultant=request.user_obj)
        elif request.POST.get('action') == 'multi_public':
            """私戶轉公戶"""
            obj.update(consultant=None)
        return redirect('major:my_customer')

    def search(self, field_name):
        """
        模糊查詢,有chioce的字段另做判斷
        :param field_name: 字段列表
        :return:
        """
        query = self.request.GET.get('query', '')
        q = Q()
        q.connector = 'OR'
        for field in field_name:
            q.children.append(Q(('{}__contains'.format(field), query)))
        return q
View Code

模板:

{% extends 'master.html' %}
{% block content %}
    <div class="pull-left btn-sm" style="margin: 0 0;padding: 0 0"><a class="btn btn-primary form-control"
                                      href="{% url 'major:add_customer' %}">新增</a>
    </div>
    <form action="" class="form-inline pull-right">
        <input class="form-control" type="text" name="query">
        <button class="btn btn-primary">搜索</button>
    </form>
    <form action="" method="post" name="" class="form-inline">
        {% csrf_token %}
    <div class="pull-right">
        <select name="action" class="form-control">
            <option value="multi_private">公戶轉私戶</option>
            <option value="multi_public">私戶轉公戶</option>
        </select>
        <button class="btn btn-primary btn-sm">提交</button>
    </div>
        <table class="table table-bordered table-hover " style="margin-bottom: 0 ">
            <thead>
            <tr>
                <th>選擇</th>
                <th>序號</th>
                <th>姓名</th>
                <th>QQ</th>
                <th>性別</th>
                <th>手機號</th>
                <th>出生日期</th>
                <th>最后跟進日期</th>
                <th>預計再次跟進日期</th>
                <th>銷售</th>
                <th>狀態</th>
                <th>已報班級</th>
                <th>操作</th>
            </tr>
            </thead>
            <tbody>
            {% for obj in customer_obj %}
                <tr>
                    <td><input type="checkbox" name="pk" value="{{ obj.pk }}"></td>
                    <td>{{ forloop.counter }}</td>
                    <td>{{ obj.name }}</td>
                    <td>{{ obj.qq }}</td>
                    <td>{{ obj.get_sex_display }}</td>
                    <td>{{ obj.phone }}</td>
                    <td>{{ obj.birthday }}</td>
                    <td>{{ obj.last_consult_date }}</td>
                    <td>{{ obj.next_date }}</td>
                    <td>{{ obj.consultant }}</td>
                    <td>{{ obj.get_status_display }}</td>
                    <td>{% for class in obj.class_list.all %}
                        {% if forloop.last %}
                            {{ class }}
                        {% else %}
                            {{ class }}、
                        {% endif %}

                    {% endfor %}
                    </td>
                    <td>
                        <button><a href="{% url 'major:edit_customer' obj.pk %}">編輯</a></button>
                        <button>刪除</button>
                    </td>
                </tr>
            {% endfor %}
            </tbody>
        </table>
        {{ pag_obj.page_html|safe }}

    </form>

{% endblock %}
View Code

前面我們說到request.GET是一個queryDict,且里面保存的是GET請求的參數,那我們是不是也可以對里面的參數進行修改呢?

先來嘗試一下:

 

 

 通過瀏覽器發送請求,結果如下:

 

 

 查看后台報錯:

 

 

查看拋出異常的代碼

 

 

 修改一下__mutable參數

 

 再次訪問:

 

 頁面是訪問正常了,但我們這里的分頁跳轉的時候,還是並未將query查詢參數加進去,接下來我要進行對自定義的分頁器進行修改,自定義分頁器文章鏈接:https://www.cnblogs.com/kindvampire/p/12342484.html

理一下思路:目前我們要達到的效果是:當前端加入了查詢參數時,后端將查詢的數據返回,並且進行分頁,分頁的頁碼跳轉鏈接上面擁有前面輸入的查詢參數query。

如何做:將查詢參數自動添加到頁碼跳轉鏈接中。

先附上之前寫的自定義分頁器代碼:

class Pagination(object):
    def __init__(self, page, page_count, per_num=10, max_show=10):
        """
        :param page:前端傳入的頁碼
        :param page_count: 分頁的數據總條數
        :param per_num: 每頁顯示的數據條數
        :param max_show: 每頁顯示的最大頁碼數
        """
        self.page = page
        self.page_count = page_count
        self.per_num = per_num
        self.max_show = max_show

        try:
            self.page = int(self.page)
            if self.page <= 0:
                self.page = 1
        except Exception:
            self.page = 1

        """
        索引切片
        page   起始   終止
      0     10
      10    20
      20    30
        """
        # 數據切片起始值
        self.start = (self.page - 1) * self.per_num
        # 數據切片終止值
        self.end = self.page * self.per_num
        # 總頁碼數
        self.sum_page, more = divmod(self.page_count, self.per_num)
        if more:
            self.sum_page += 1
        # 前端循環的a標簽的值
        self.num = [i for i in range(1, self.sum_page + 1)]

        # 頁碼限制
        # 頁碼的切片的起始值
        self.page_start = 0
        # 頁碼切片的終止值
        self.page_end = self.max_show
        # 頁碼的偏移量
        half_page = self.max_show // 2

        if self.page < self.max_show:
            """
            如果page在第一頁,什么都不做,即page_start=0,page_end=max_show
            """
            pass

        if self.page > self.sum_page:
            """
            如果page參數大於總頁碼數,跳轉到最后一頁,針對於url上面手動輸入page參數
            """
            # 終止值為最后一頁
            self.page_end = self.sum_page
            # 起始值向前偏移half_page
            self.page_start = self.page_end - half_page
            # 數據部分也需要設定,不然顯示為空
            self.start = (self.sum_page - 1) * self.per_num
            self.end = self.sum_page * self.per_num
            # 當page大於總頁碼時,讓page等於最大的頁碼,為了給最后一頁添加激活樣式
            self.page = self.sum_page

        else:
            """
            page當前頁碼即不在第一頁,也不在最后一頁(或超過最后一頁),起始值向前偏移half_page,向后偏移half_page
            """
            self.page_start = self.page - half_page
            self.page_end = self.page + half_page

        # 當總頁碼數小於每頁最大展示頁碼數量時,無論在那一頁都展示全部的頁碼
        if self.max_show > self.sum_page:
            self.page_start = 0
            self.page_end = self.sum_page
        # 當傳入的總數據量小於等於0時(主要是等於零),會報錯,通過下面的判斷來解決這個問題
        if page_count <= 0:
            self.start = 0
            self.end = 0

    def page_html(self):
        num = self.num[self.page_start:self.page_end]
        html_list = []
        # 頁碼樣式的頭
        html_list.append(
            '<div class="pull-left form-group col-sm-7" style="padding-left:0"><nav  aria-label="Page navigation"><ul class="pagination">')
        # 當前頁為1時,添加上一頁禁用樣式disabled
        if self.page == 1:
            html_list.append(
                '<li class="disabled"><a aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>')
        else:
            html_list.append(
                '<li ><a href="?page={}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.page - 1))
        for n in num:
            if self.page == n:
                html_list.append('<li class="active"><a>{}</a></li>'.format(n))
            else:
                html_list.append('<li><a href="?page={}">{}</a></li>'.format(n, n))

        # 當前頁為最后一頁時,添加下一頁禁用樣式disabled
        if self.page == self.sum_page:
            html_list.append(
                '<li class="disabled"><a aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>')
        else:
            html_list.append(
                '<li><a href="?page={}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.page + 1))
        # 頁碼樣式的尾
        html_list.append('</ul></nav></div>')
        # 將列表中的html代碼拼接起來
        html_list = "".join(html_list)
        return html_list
View Code

下面進行操作:

第一步:將request.GET傳入到分頁器中,為了避免出現分頁其中修改request.GET而影響到后續代碼的結果,我們需要深拷貝一份request.GET,然后傳入分頁器(我們需要注意的是:分頁器需要的是request.GET里面的信息,並不需要對原request.GET里的參數進行修改)

注意點:QueryDict中自己定義了copy()方法(深拷貝),且mutable參數設置為True,不需要我們去設置,所以直接調用即可。

 

 視圖函數:

 

 分頁器:

 

 

 第二步:自動生成頁碼鏈接(先手動修改頁碼,然后在頁碼鏈接部分上調用urlencode方法,自動生成頁碼鏈接參數。)

代碼修改如下:

# -*- coding: utf-8 -*-
# __author__ = "maple"
from django.http.request import QueryDict


class Pagination(object):
    def __init__(self, page, page_count, qd=None, per_num=10, max_show=10):
       ......
    def page_html(self):
        num = self.num[self.page_start:self.page_end]
        html_list = []
        # 頁碼樣式的頭
        html_list.append(
            '<div class="pull-left form-group col-sm-7" style="padding-left:0"><nav  aria-label="Page navigation"><ul class="pagination">')
        # 當前頁為1時,添加上一頁禁用樣式disabled
        if self.page == 1:
            self.qd['page'] = 1
            html_list.append(
                '<li class="disabled"><a href="?{}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        else:
            self.qd['page'] = self.page -1
            html_list.append(
                '<li ><a href="?{}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        for n in num:
            self.qd['page'] = n
            if self.page == n:
                html_list.append('<li class="active"><a>{}</a></li>'.format(n))
            else:
                html_list.append('<li><a href="?{}">{}</a></li>'.format(self.qd.urlencode(), n))

        # 當前頁為最后一頁時,添加下一頁禁用樣式disabled
        if self.page == self.sum_page:
            self.qd['page'] = self.sum_page
            html_list.append(
                '<li class="disabled"><a href="?{}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        else:
            self.qd['page'] = self.page + 1
            html_list.append(
                '<li><a href="?{}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        # 頁碼樣式的尾
        html_list.append('</ul></nav></div>')
        # 將列表中的html代碼拼接起來
        html_list = "".join(html_list)
        return html_list
View Code

到這里基本就完成了分頁保留查詢參數的功能。你可能到這里就有點迷糊了,在分頁器中調用urlencode的時候沒有添加查詢參數啊,它是怎么上去的?注意:我們在發送請求的時候,直接就將query參數添加進去了,所以每一次urlencode進行參數拼接時會將query查詢參數拼接進去並自動生成a標簽的鏈接地址,當然,當query為空的時候,它也是不會添加進去的。

注意:這里有一點bug,當我們不想傳入request.GET.copy()的時候是會報錯的因為qd默認是None,所不能調用urlencode,我們要避免出現這個bug,就需要手動生成一個QueryDict,並設置mutable為True。操作如下:

 

 

附:自定義分頁器代碼及使用

# -*- coding: utf-8 -*-
# __author__ = "maple"
from django.http.request import QueryDict


class Pagination(object):
    def __init__(self, page, page_count, qd=None, per_num=10, max_show=10):
        """
        :param page:前端傳入的頁碼
        :param page_count: 分頁的數據總條數
        :param per_num: 每頁顯示的數據條數
        :param max_show: 每頁顯示的最大頁碼數
        """
        self.page = page
        self.page_count = page_count
        self.per_num = per_num
        self.max_show = max_show
        self.qd = qd
        if not qd:
            qd = QueryDict(mutable=True)
            self.qd = qd

        try:
            self.page = int(self.page)
            if self.page <= 0:
                self.page = 1
        except Exception:
            self.page = 1

        """
        索引切片
        page   起始   終止
        1       0     10
        2       10    20
        3       20    30
        """
        # 數據切片起始值
        self.start = (self.page - 1) * self.per_num
        # 數據切片終止值
        self.end = self.page * self.per_num
        # 總頁碼數
        self.sum_page, more = divmod(self.page_count, self.per_num)
        if more:
            self.sum_page += 1
        # 前端循環的a標簽的值
        self.num = [i for i in range(1, self.sum_page + 1)]

        # 頁碼限制
        # 頁碼的切片的起始值
        self.page_start = 0
        # 頁碼切片的終止值
        self.page_end = self.max_show
        # 頁碼的偏移量
        half_page = self.max_show // 2

        if self.page < self.max_show:
            """
            如果page在第一頁,什么都不做,即page_start=0,page_end=max_show
            """
            pass

        if self.page > self.sum_page:
            """
            如果page參數大於總頁碼數,跳轉到最后一頁,針對於url上面手動輸入page參數
            """
            # 終止值為最后一頁
            self.page_end = self.sum_page
            # 起始值向前偏移half_page
            self.page_start = self.page_end - half_page
            # 數據部分也需要設定,不然顯示為空
            self.start = (self.sum_page - 1) * self.per_num
            self.end = self.sum_page * self.per_num
            # 當page大於總頁碼時,讓page等於最大的頁碼,為了給最后一頁添加激活樣式
            self.page = self.sum_page

        else:
            """
            page當前頁碼即不在第一頁,也不在最后一頁(或超過最后一頁),起始值向前偏移half_page,向后偏移half_page
            """
            self.page_start = self.page - half_page
            self.page_end = self.page + half_page

        # 當總頁碼數小於每頁最大展示頁碼數量時,無論在那一頁都展示全部的頁碼
        if self.max_show > self.sum_page:
            self.page_start = 0
            self.page_end = self.sum_page
        # 當傳入的總數據量小於等於0時(主要是等於零),會報錯,通過下面的判斷來解決這個問題
        if page_count <= 0:
            self.start = 0
            self.end = 0

    def page_html(self):
        num = self.num[self.page_start:self.page_end]
        html_list = []
        # 頁碼樣式的頭
        html_list.append(
            '<div class="pull-left form-group col-sm-7" style="padding-left:0"><nav  aria-label="Page navigation"><ul class="pagination">')
        # 當前頁為1時,添加上一頁禁用樣式disabled
        if self.page == 1:
            self.qd['page'] = 1
            html_list.append(
                '<li class="disabled"><a href="?{}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        else:
            self.qd['page'] = self.page -1
            html_list.append(
                '<li ><a href="?{}" aria-label="Previous"><span aria-hidden="true">&laquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        for n in num:
            self.qd['page'] = n
            if self.page == n:
                html_list.append('<li class="active"><a>{}</a></li>'.format(n))
            else:
                html_list.append('<li><a href="?{}">{}</a></li>'.format(self.qd.urlencode(), n))

        # 當前頁為最后一頁時,添加下一頁禁用樣式disabled
        if self.page == self.sum_page:
            self.qd['page'] = self.sum_page
            html_list.append(
                '<li class="disabled"><a href="?{}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        else:
            self.qd['page'] = self.page + 1
            html_list.append(
                '<li><a href="?{}" aria-label="Next"><span aria-hidden="true">&raquo;</span></a></li>'.format(
                    self.qd.urlencode()))
        # 頁碼樣式的尾
        html_list.append('</ul></nav></div>')
        # 將列表中的html代碼拼接起來
        html_list = "".join(html_list)
        return html_list
View Code

使用:

from utils import pagination # 導入自定義分頁器
page = request.GET.get('page', 1) #獲取當前頁碼參數
pag_obj = pagination.Pagination(page=page, page_count=customer_obj.count(),qd=request.GET.copy(), per_num=3, max_show=10) #實例化分頁器對象
customer_obj = customer_obj[pag_obj.start:pag_obj.end] # 對queryset對象進行分頁

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM