python 全棧開發,Day115(urlencode,批量操做,快速搜索,保留原搜索條件,自定義分頁,拆分代碼)

今日內容前戲

靜態字段和字段

先來看下面一段代碼html

class Foo:
    x = 1  # 類變量、靜態字段、靜態屬性
    
    def __init__(self):
        y = 6 # 實例變量、字段、對象屬性
    
    # 實例方法 
    def func(self):
        pass 
    
    # 靜態方法
    @staticmethod
    def func():
        pass 
    
    # 類方法
    @classmethod
    def func():
        pass 
    
    @property
    def start(self)
        pass 
View Code

官方說法:x稱之爲 類變量  y稱之爲 實例變量java

在java和C#中,分別叫 靜態字段字段python

 

函數名

獲取函數的名字,可經過 函數.__name__  方法獲取。git

那麼在django模板中,不能使用雙下方法。會報錯!github

 

urlencode

什麼是urlencode

URL編碼(URL encoding),也稱做百分號編碼(Percent-encoding), 是特定上下文的統一資源定位符 (URL)的編碼機制。web

爲何要用urlencode

發現如今幾乎全部的網站都對url中的漢字和特殊的字符,進行了urlencode操做,也就是:數據庫

http://hi.baidu.com/%BE%B2%D0%C4%C0%CF%C8%CB/creat/blog/django

這個樣子,中間%形式的。注意:urlencode是一種編碼,它不是加密方式flask

 

url轉義是爲了符合url的規範,由於在標準的url規範中中文和不少的字符是不容許出如今url中的。後端

URLEncode就是將URL中特殊部分進行編碼。URLDecoder就是對特殊部分進行解碼。

 

由於當字符串數據以url的形式傳遞給web服務器時,字符串中是不容許出現空格和特殊字符的

譬如:你要傳的字符串數據時name=lisi&wangwu  這裏的lisi&wangwu是一個字符串  可是服務器只會將lisi識別出來

因此要用到urlencode對這個字符串進行編碼

 

協調開發時,所使用的編碼不一樣。好比:A發送參數攜帶中文時,使用gbk。B接收時,使用utf-8解碼時,就會出現亂碼。

那麼A發送url的參數使用了URLEncode編碼,B接收時使用URLDecoder解碼,就不會出現亂碼了!

 

舉例:

有一個字典

info = {'k1':'v1','k2':'v2'}

須要轉換爲

k1=v1&k2=v2

怎麼轉換?

第一種方法:使用for循環

info = {'k1':'v1','k2':'v2'}
list_1 = []
for k,v in info.items():
    # print('{}={}'.format(k,v))
    list_1.append('{}={}'.format(k,v))

res = '&'.join(list_1)
print(res)
View Code

執行輸出:k1=v1&k2=v2

第一種方法:urlencode

from urllib.parse import urlencode
info = {'k1':'v1','k2':'v2'}
print(urlencode(info))
View Code

以上能夠看出,使用urlencode更方便!

字典有多少個key-value,均可以方便的轉換!

 

QueryDict

在django中,request.GET的值類型,是什麼呢?是QueryDict

新建一個項目untitled3,注意:django的版本爲1.11

修改urls.py,增長路徑index

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),
]
View Code

修改views.py,增長index視圖函數

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    print(request.GET,type(request.GET))
    return HttpResponse('ok')
View Code

啓動django項目,訪問index頁面

查看Pycharm控制檯輸出:

<QueryDict: {}> <class 'django.http.request.QueryDict'>

它是一個QueryDict類型

 

在url上面添加參數,訪問url

http://127.0.0.1:8000/index/?name=xiao&age=20

查看Pycharm控制檯輸出:

<QueryDict: {'age': ['20'], 'name': ['xiao']}> <class 'django.http.request.QueryDict'>

 

修改views.py,導入QueryDict,查看源碼

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    print(request.GET)

    v = request.GET.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

它內部調用了urlencode,具體位置,我找不到了...

 

刷新頁面,查看Pycharm控制檯輸出:

<QueryDict: {'name': ['xiao'], 'age': ['20']}>
name=xiao&age=20

request.GET.urlencode,能夠直接調用!

 

能不能在request.GET中,添加一個值呢?

修改views.py,增長一個值

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    print(request.GET)
    request.GET['k1'] = 666  # 增長一個值
    v = request.GET.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

刷新頁面,效果以下:

提示:  這個QueReDICT實例是不可變的,這個是django安全策略作的

 

若是必定要修改,須要設置一個參數

request.GET._mutable = True

必定要重啓django,不然不生效!刷新頁面,效果以下:

查看Pycharm控制檯輸出:

<QueryDict: {'age': ['20'], 'name': ['xiao']}>
k1=666&age=20&name=xiao

發現k1已經添加進去了!

 

爲了不對後續的程序有影響,須要使用深copy

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)
    
    params = copy.deepcopy(request.GET)  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    v = params.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

重啓django項目,刷新頁面,效果同上!

查看Pycharm控制檯輸出,效果同上!

 

QueryDict 源碼裏面,提供了方法__deepcopy__ ,它也是作深copy的

def __deepcopy__(self, memo):
    result = self.__class__('', mutable=True, encoding=self.encoding)
    memo[id(self)] = result
    for key, value in six.iterlists(self):
        result.setlist(copy.deepcopy(key, memo), copy.deepcopy(value, memo))
    return result

查看request.GET.copy()的源碼,它實際上,就是調用了__deepcopy__

def copy(self):
    """Returns a mutable copy of this object."""
    return self.__deepcopy__({})

 

修改views.py,改用request.GET.copy()

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)

    params = request.GET.copy()  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    v = params.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

重啓django項目,刷新頁面,效果同上!

查看Pycharm控制檯輸出,效果同上!

 

 

修改views.py,增長一個列表

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)

    params = request.GET.copy()  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    params['k2'] = [11,12]  # 增長一個列表
    v = params.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

刷新頁面,查看Pycharm控制檯輸出:

<QueryDict: {'age': ['20'], 'name': ['xiao']}>
name=xiao&k2=%5B11%2C+12%5D&k1=666&age=20

發現k2=%5B11%2C+12%5D,這並非咱們想要的結果!

 

列表須要使用setlist才行

修改views.py,使用setlist

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)

    params = request.GET.copy()  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    params.setlist('k4',[11,12])  # 增長一個列表
    v = params.urlencode()
    print(v)

    return HttpResponse('ok')
View Code

重啓django項目,刷新頁面。查看Pycharm控制檯輸出:

<QueryDict: {'name': ['xiao'], 'age': ['20']}>
k1=666&k4=11&k4=12&name=xiao&age=20

能夠看到k4=11&k4=12,它分別設置了2個值!

 

那麼獲取k4使用getlist

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)

    params = request.GET.copy()  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    params.setlist('k4',[11,12])  # 增長一個列表
    print(params.get('k1'))
    print(params.getlist('k4'))  # 獲取列表,使用getlist
    v = params.urlencode()
    print(v)


    return HttpResponse('ok')
View Code

刷新頁面,查看Pycharm控制檯輸出:

<QueryDict: {'name': ['xiao'], 'age': ['20']}>
666
[11, 12]
k4=11&k4=12&k1=666&name=xiao&age=20

 

 在列表中,增長一個值,使用append

修改views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict
    import copy
    print(request.GET)

    params = request.GET.copy()  # 使用深copy
    params._mutable = True  # 容許修改
    params['k1'] = 666  # 增長一個值
    params.setlist('k4',[11,12])  # 增長一個列表

    old = params.getlist('k4')  # 獲取列表
    old.append('v4')  # 最加一個值
    params.setlist('k4',old)  # 從新設置
    v = params.urlencode()
    print(v)


    return HttpResponse('ok')
View Code

刷新頁面,查看Pycharm控制檯輸出:

<QueryDict: {'name': ['xiao'], 'age': ['20']}>
name=xiao&k1=666&age=20&k4=11&k4=12&k4=v4

能夠發現,k4有3個值

 

保留原來搜索條件

先來訪問一個連接:

http://127.0.0.1:8000/index/?name=xiao&age=20

須要在視圖函數中的request.GET中,添加一個值_filter。用來保存原來的搜索條件

_filter = "name=xiao&age=20"

最後變成QueryDict,如何操做?

 

修改views.py

from django.shortcuts import render,HttpResponse

# Create your views here.
def index(request):
    from django.http.request import QueryDict

    url_params_str = request.GET.urlencode()
    # mutable=True 表示可修改
    query_dic = QueryDict(mutable=True)
    # 添加一個key爲_filter
    query_dic['_filter'] = url_params_str
    print(query_dic)

    # 從新編碼
    new_params = query_dic.urlencode()
    print(new_params)


    return HttpResponse('ok')
View Code

刷新頁面,查看Pycharm控制檯輸出:

<QueryDict: {'_filter': ['name=xiao&age=20']}>
_filter=name%3Dxiao%26age%3D20

最後一個值,使用urlencode編碼了

 

舉例:

假設一個場景,先搜索到了一些學生。注意:此時url是有搜索條件的

點擊添加按鈕,跳轉到添加頁面。注意:此時的url帶有原搜索條件

添加完成後,跳轉到原來的頁面。注意:此時的url帶有原搜索條件

 

修改urls.py,增長路徑

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^add_stu/', views.add_stu),
]
View Code

 

修改views.py,增肌視圖函數

from django.shortcuts import render,HttpResponse,redirect

# Create your views here.
def index(request):
    from django.http.request import QueryDict

    url_params_str = request.GET.urlencode()
    # mutable=True 表示可修改
    query_dic = QueryDict(mutable=True)
    # 添加一個key爲_filter
    query_dic['_filter'] = url_params_str
    print(query_dic)

    # 從新編碼
    new_params = query_dic.urlencode()
    print(new_params)

    # 跳轉地址
    target_url = "/add_stu?%s" %new_params
    print(target_url)
    # 渲染一個a標籤
    return HttpResponse('<a href="%s">添加學生</a>'%target_url)

def add_stu(request):
    if request.method == "GET":
        return render(request,"add_stu.html")

    # 接收到數據,保存到數據庫...
    # 獲取搜索條件
    origin_params = request.GET.get('_filter')
    # 返回地址,保留原搜索添加
    back_url = "/index/?%s" %origin_params
    # 重定向頁面
    return redirect(back_url)
View Code

 重啓django程序,訪問頁面

注意:這裏直接訪問的是帶有搜索條件的

http://127.0.0.1:8000/index/?name=xiao&age=20

效果以下:

點擊添加學生,頁面跳轉

注意:這裏面的_filter,就是原來的搜索條件

點擊提交按鈕,頁面跳轉

此時頁面:仍是帶有原來的搜索條件

 

Q查詢高級用法

 通常頁面使用的搜索功能,都是使用了模糊搜索

好比:查詢name包含 "大"或者email包含 "qq"的記錄

q = Q()
q.connecter = "OR"  # 使用OR做爲鏈接符
# 合併條件進行查詢,__contains表示使用like查詢
q.children.append(('name__contains', ''))
q.children.append(('email__contains', 'qq'))

它至關於 name like '%大%' OR email like '%qq%'

 

1、批量操做

務必下載github代碼:

https://github.com/987334176/luffy_stark/archive/v1.1.zip

由於下面的內容,都是這份代碼來修改的!

 

修改stark-->templates-->stark-->changelist.html,增長select標籤

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            <div class="form-group">
                <select name="action" class="form-control" style="min-width: 200px;">
                    <option>請選擇功能</option>
                    <option value="1">批量刪除</option>
                    <option value="2">初始化</option>
                </select>
                <input class="btn btn-primary" type="submit" value="執行">
            </div>
        </form>
        <table class="table table-bordered">
            <thead>
            <tr>
                {% for item in header_list %}
                    <th>{{ item }}</th>
                {% endfor %}

            </tr>
            </thead>
            <tbody>
            {% for row_list in body_list %}
                <tr>
                    {% for col in row_list %}
                        <td>{{ col }}</td>
                    {% endfor %}

                </tr>
            {% endfor %}
            </tbody>
        </table>

    </div>



{% endblock %}
View Code

 

修改 app01-->stark.py,註釋部分代碼。增長複選框顯示

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartModelForm(forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = "__all__"

    def clean_name(self):  # 定義鉤子
        # print(self.cleaned_data['name'])
        return self.cleaned_data['name']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
    # model_form_class = DepartModelForm
    # def get_add_btn(self):  # 返回None,表示不顯示添加按鈕
    #     pass
    # def changelist_view(self, request):  # 重寫changelist_view方法
    #     # 渲染自定義的列表頁面
    #     return render(request,'stark/custom_list.html')
    # def get_urls(self):  # 自定義路由
    #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
    #
    #     urlpatterns = [
    #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
    #     ]
    #     return urlpatterns

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)
View Code

 

訪問頁面: http://127.0.0.1:8000/stark/app01/userinfo/list/

點擊下拉菜單,效果以下:

下拉框裏面的元素應該是動態的,它是可配置的,每一個選項包含了一個id。

若是使用表單提交,那麼表單數據就很大了

 

修改stark-->server-->stark.py,添加變量action_list。用來定義要批量操做的選項

爲了防止列表來回更改,致使列表元素重複。使用extend

添加multi_delete和multi_init方法,將action_list傳給模板

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self):  # 批量刪除
        pass

    def multi_init(self):  # 批量初始化
        pass

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現

        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改app01-->stark.py,定義action_list

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartModelForm(forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = "__all__"

    def clean_name(self):  # 定義鉤子
        # print(self.cleaned_data['name'])
        return self.cleaned_data['name']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
    # model_form_class = DepartModelForm
    # 批量操做
    action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
    # def get_add_btn(self):  # 返回None,表示不顯示添加按鈕
    #     pass
    # def changelist_view(self, request):  # 重寫changelist_view方法
    #     # 渲染自定義的列表頁面
    #     return render(request,'stark/custom_list.html')
    # def get_urls(self):  # 自定義路由
    #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
    #
    #     urlpatterns = [
    #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
    #     ]
    #     return urlpatterns

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)
View Code

 

修改 stark-->templates-->stark-->changelist.html,使用for循環action_list

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            <div class="form-group">
                <select name="action" class="form-control" style="min-width: 200px;">
                    <option>請選擇功能</option>
                    {% for func in action_list %}
                        <option value="1">{{ func.__name__ }}</option>
                    {% endfor %}
                </select>
                <input class="btn btn-primary" type="submit" value="執行">
            </div>
        </form>
        <table class="table table-bordered">
            <thead>
            <tr>
                {% for item in header_list %}
                    <th>{{ item }}</th>
                {% endfor %}

            </tr>
            </thead>
            <tbody>
            {% for row_list in body_list %}
                <tr>
                    {% for col in row_list %}
                        <td>{{ col }}</td>
                    {% endfor %}

                </tr>
            {% endfor %}
            </tbody>
        </table>

    </div>



{% endblock %}
View Code

 

重啓django,查看頁面:http://127.0.0.1:8000/stark/app01/depart/list/

效果以下:

提示  變量和屬性不能從下劃線開始

怎麼辦呢?讓後端把函數名傳過來,使用列表生成式

 

修改stark-->server-->stark.py,使用列表生成式

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self):  # 批量刪除
        pass

    def multi_init(self):  # 批量初始化
        pass

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        action_list = [ func.__name__ for func in action_list ]
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改 stark-->templates-->stark-->changelist.html,修改變量名

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            <div class="form-group">
                <select name="action" class="form-control" style="min-width: 200px;">
                    <option>請選擇功能</option>
                    {% for name in action_list %}
                        <option value="1">{{ name }}</option>
                    {% endfor %}
                </select>
                <input class="btn btn-primary" type="submit" value="執行">
            </div>
        </form>
        <table class="table table-bordered">
            <thead>
            <tr>
                {% for item in header_list %}
                    <th>{{ item }}</th>
                {% endfor %}

            </tr>
            </thead>
            <tbody>
            {% for row_list in body_list %}
                <tr>
                    {% for col in row_list %}
                        <td>{{ col }}</td>
                    {% endfor %}

                </tr>
            {% endfor %}
            </tbody>
        </table>

    </div>



{% endblock %}
View Code

刷新頁面,效果以下:

它是英文的,要中文展現,怎麼搞?

 

python一切皆對象,能夠爲函數添加一個屬性。屬性名無所謂!

新建一個a.py文件,內容以下:

def func():
    print(123)

func.aa = '批量刪除'

print(func.aa)
View Code

執行輸出:批量刪除

 

一樣的道理,給函數multi_delete和multi_init,添加屬性text

在列表生成式中,獲取text屬性

修改stark-->server-->stark.py,

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self):  # 批量刪除
        pass

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self):  # 批量初始化
        pass

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取text屬性
        action_list = [ func.text for func in action_list ]
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

重啓django,刷新頁面,效果以下:

光有中文,沒法知道它對應的是哪一個函數?value值都是1

 

修改stark-->server-->stark.py,更改列表生成式的元素爲字典

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe('<input type="checkbox" name="pk" values="%s"/>' %row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self):  # 批量刪除
        pass

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self):  # 批量初始化
        pass

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改 stark-->templates-->stark-->changelist.html,value值爲函數名

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            <div class="form-group">
                <select name="action" class="form-control" style="min-width: 200px;">
                    <option>請選擇功能</option>
                    {% for item in action_list %}
                        <option value="{{ item.name }}">{{ item.text }}</option>
                    {% endfor %}
                </select>
                <input class="btn btn-primary" type="submit" value="執行">
            </div>
        </form>
        <table class="table table-bordered">
            <thead>
            <tr>
                {% for item in header_list %}
                    <th>{{ item }}</th>
                {% endfor %}

            </tr>
            </thead>
            <tbody>
            {% for row_list in body_list %}
                <tr>
                    {% for col in row_list %}
                        <td>{{ col }}</td>
                    {% endfor %}

                </tr>
            {% endfor %}
            </tbody>
        </table>

    </div>



{% endblock %}
View Code

刷新頁面,效果以下:

能夠看到value值已經渲染出來了!

 

那麼問題來了,table表格的數據,沒有包含到form表單裏面

修改 stark-->templates-->stark-->changelist.html,更改form表單

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            <div class="form-group">
                <select name="action" class="form-control" style="min-width: 200px;">
                    <option>請選擇功能</option>
                    {% for item in action_list %}
                        <option value="{{ item.name }}">{{ item.text }}</option>
                    {% endfor %}
                </select>
                <input class="btn btn-primary" type="submit" value="執行">
            </div>

            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}

                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}

                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>



{% endblock %}
View Code

 

修改stark-->server-->stark.py,修改changelist_view方法,接收post數據

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self):  # 批量刪除
        pass

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self):  # 批量初始化
        pass

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == "POST":
            print(request.POST)  # 獲取post數據


        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

刷新頁面,選擇批量刪除,勾選幾條數據。點擊提交

查看Pycharm控制檯輸出:

<QueryDict: {'csrfmiddlewaretoken': ['FtQPfzOYfkiOxGx80QYcIFxqT9wqSpgwWYO1M9rR9lUzkLwDgO6ud260AfIUkJnK'], 'pk': ['1', '2', '3'], 'action': ['multi_delete']}>

獲取獲得數據了!

接下來須要根據獲取的方法名來執行方法。怎麼判斷呢?使用if?若是有多個方法呢?

這個時候,應該使用反射

 

修改stark-->server-->stark.py,使用反射執行對應的方法。修改那2個方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self,request):  # 批量刪除
        print('批量刪除')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == "POST":
            action_name = request.POST.get('action')
            getattr(self,action_name)(request)


        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

刷新頁面,再次選擇批量刪除,勾選幾條數據,點擊提交!

查看Pycharm控制檯輸出: 批量刪除

 

假若有不法分子,修改html呢?

 

爲了不攻擊,使用字典判斷,由於字典查詢快

修改stark-->server-->stark.py,增長方法get_action_dict

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self,request):  # 批量刪除
        print('批量刪除')
        return HttpResponse('批量刪除')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response


        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 刷新頁面,點擊匹配初始化,仍是會跳轉到當前頁面。由於此方法,沒有返回值。

點擊批量刪除,頁面效果以下:

 

修改stark-->server-->stark.py,修改multi_delete方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response


        # 根據排序列表進行排序
        queryset = self.model_class.objects.all().order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

刷新頁面,選中一條數據,點擊提交

 

發現少了一條數據

 

訪問其它頁面,好比: http://127.0.0.1:8000/stark/app01/userinfo/list/

發現下拉框是空的,那麼它就不該該顯示!

 

修改stark-->templates-->stark-->changelist.html,添加if判斷

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        <form class="form-inline" method="post">
            {% csrf_token %}
            {% if action_list %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min-width: 200px;">
                        <option>請選擇功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="執行">
                </div>
            {% endif %}

            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}

                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}

                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>



{% endblock %}
View Code

刷新頁面,效果以下:

多選框,沒有了!

其餘頁面,想使用批量操做,定義action_list變量,就能夠了!

 

2、快速搜索

 搜索什麼內容,取哪一個字段搜,都是能夠定製的!

修改stark-->server-->stark.py,增長search_list變量。定義name和tel能夠搜索!

增長方法get_search_list方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    search_list = ['name','tel']  # 固定搜索字段

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        from django.db.models import Q
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR" # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))


        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入add_btn
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

訪問url,要帶上q參數

http://127.0.0.1:8000/stark/app01/depart/list/?q=總

效果以下:

去掉參數,有2條數據

這樣體驗很差,要用戶輸入才行!

 

修改stark-->server-->stark.py,給changelist.html傳入參數q

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    search_list = ['name','tel']  # 固定搜索字段

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        from django.db.models import Q
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR" # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))


        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改 stark-->templates-->stark-->changelist.html,增長輸入框

即便頁面刷新,input輸入框還會保留搜索的關鍵字!

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {#添加按鈕#}
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        {#搜索框#}
        <div style="float: right;">
            <form method="GET" class="form-inline">
                <div class="form-group">
                    <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="關鍵字搜索">
                    <button class="btn btn-primary" type="submit">
                        <i class="fa fa-search" aria-hidden="true"></i>
                    </button>
                </div>
            </form>
        </div>

        <form class="form-inline" method="post">
            {% csrf_token %}
            {#批量操做#}
            {% if action_list %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min-width: 200px;">
                        <option>請選擇功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="執行">
                </div>
            {% endif %}
            {#使用table展現數據#}
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}

                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}

                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>



{% endblock %}
View Code

 

訪問url:  http://127.0.0.1:8000/stark/app01/depart/list/

效果以下:

輸入關鍵字534,點擊搜索按鈕。那麼url會自動帶上參數,這個是GET原本就有的功能。由於GET參數在url裏面!

同時結果只有一條

 

若是要搜索負責人呢?注意:這個字段涉及到跨表了。那麼填寫search_list參數時,要帶上下劃線

修改stark-->server-->stark.py,修改search_list變量

# 固定搜索字段,若是是跨表字段,要按照ORM語法來
search_list = ['name','tel','user__username']

刷新頁面,從新制定關鍵字

 

上面是爲了方便調試,把搜索字段給固定死了。它應該是能夠定製的!

修改stark-->server-->stark.py,將search_list變量設置爲空

# 搜索字段,若是是跨表字段,要按照ORM語法來
search_list = []

修改 app01-->stark.py,指定變量search_list

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartModelForm(forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = "__all__"

    def clean_name(self):  # 定義鉤子
        # print(self.cleaned_data['name'])
        return self.cleaned_data['name']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user']
    # model_form_class = DepartModelForm
    # 批量操做
    action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
    # 搜索關鍵字
    # 固定搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = ['name', 'tel', 'user__username']

    # def get_add_btn(self):  # 返回None,表示不顯示添加按鈕
    #     pass
    # def changelist_view(self, request):  # 重寫changelist_view方法
    #     # 渲染自定義的列表頁面
    #     return render(request,'stark/custom_list.html')
    # def get_urls(self):  # 自定義路由
    #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
    #
    #     urlpatterns = [
    #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
    #     ]
    #     return urlpatterns

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)
View Code

重啓django,刷新頁面,效果同上!

 

若是沒有定義search_list變量,那麼search_list默認爲空,它不該該展現!

修改stark-->server-->stark.py,給changelist.html傳入參數search_list

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        from django.db.models import Q
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR" # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))


        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改 stark-->templates-->stark-->changelist.html,增長if 判斷

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {#添加按鈕#}
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        {#搜索框#}
        {% if search_list %}
            <div style="float: right;">
                <form method="GET" class="form-inline">
                    <div class="form-group">
                        <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="關鍵字搜索">
                        <button class="btn btn-primary" type="submit">
                            <i class="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
                </form>
            </div>
        {% endif %}

        <form class="form-inline" method="post">
            {% csrf_token %}
            {#批量操做#}
            {% if action_list %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min-width: 200px;">
                        <option>請選擇功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="執行">
                </div>
            {% endif %}
            {#使用table展現數據#}
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}

                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}

                    </tr>
                {% endfor %}
                </tbody>
            </table>
        </form>
    </div>



{% endblock %}
View Code

 

修改app01-->stark.py,註釋掉search_list變量

# 固定搜索字段,若是是跨表字段,要按照ORM語法來
# search_list = ['name', 'tel', 'user__username']

訪問url:  http://127.0.0.1:8000/stark/app01/depart/list/

搜索框就沒有了!

 

在changelist_view中,處理搜索的代碼,能夠封裝成方法。

修改stark-->server-->stark.py,增長get_search_condition方法

from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def get_search_condition(self, request):  # 根據關鍵字,組合ORM查詢語句
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR"  # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))

        return search_list, q, con

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        search_list, q, con = self.get_search_condition(request)

        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self,func):
        pass

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
            url(r'^add/$', self.add_view, name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.change_view, name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.delete_view, name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改app01-->stark.py,開啓search_list變量

# 搜索字段,若是是跨表字段,要按照ORM語法來
search_list = ['name', 'tel', 'user__username']

重啓django,刷新頁面。測試搜索功能,效果同上!

 

3、保留原搜索條件

在內容前戲部分,講到了 保留原搜索條件。關鍵點在於:搜索到結果後,跳轉其餘頁面時,url要帶上原搜索條件。

 

好比頁面默認展現的是20條數據。搜索了2個用戶,並編輯保存。跳轉的頁面應該也仍是以前2個客戶的數據,而不是20數據(返回列表首頁)

頁面上的a標籤,button按鈕。這些url是反向生成的,能不能加條件?加上原來的搜索條件?

答案是能夠的!

裝飾器

查看 stark-->server-->stark.py,StarkConfig類裏面的get_urls方法。裏面定義了4個視圖函數,它們是有request變量的。除此以外,其餘函數也須要使用request變量,怎麼辦?一個一個傳?

太麻煩了,使用裝飾器就能夠解決!

 

修改 stark-->server-->stark.py,修改StarkConfig類的__init__方法,添加變量request。

增長裝飾器wrapper。並定義self.request = request,對request從新賦值!

修改get_urls方法,應用裝飾器

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site
        # 定義request變量,用於非視圖函數使用。
        # 在wrapper裝飾器中,對這個值從新賦值!
        self.request = None
        # url中的搜索條件,存在字典中。key爲_filter
        self.back_condition_key = "_filter"

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def get_search_condition(self, request):  # 根據關鍵字,組合ORM查詢語句
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR"  # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))

        return search_list, q, con

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        search_list, q, con = self.get_search_condition(request)

        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self, func):
        @functools.wraps(func)
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
            url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)
        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)

        if not self.request.GET:
            return add_url
        param_str = self.request.GET.urlencode()  # q=嘉瑞&page=2
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)

        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})
        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})
        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

重啓django,刷新頁面,效果同上!

 

修改 stark-->server-->stark.py,修改reverse_add_url方法,增長搜索條件

其它3個方法reverse_del_url,reverse_edit_url,reverse_list_url也一樣須要添加

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site
        # 定義request變量,用於非視圖函數使用。
        # 在wrapper裝飾器中,對這個值從新賦值!
        self.request = None
        # url中的搜索條件,存在字典中。key爲_filter
        self.back_condition_key = "_filter"

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def get_search_condition(self, request):  # 根據關鍵字,組合ORM查詢語句
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR"  # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))

        return search_list, q, con

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        search_list, q, con = self.get_search_condition(request)

        # 根據排序列表進行排序
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self, func):
        @functools.wraps(func)
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
            url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)

        # 獲取當前請求的_filter參數,也就是跳轉以前的搜索條件
        origin_condition = self.request.GET.get(self.back_condition_key)
        if not origin_condition:  # 若是沒有獲取到
            return list_url  # 返回列表頁面

        # 列表地址和搜索條件拼接
        list_url = "%s?%s" % (list_url, origin_condition,)

        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)

        if not self.request.GET:  # 判斷get參數爲空
            return add_url  # 返回原url
        # request.GET的數據類型爲QueryDict
        # 對QueryDict作urlencode編碼
        param_str = self.request.GET.urlencode() # 好比q=xiao&age=20
        # 容許對QueryDict作修改
        new_query_dict = QueryDict(mutable=True)
        # 添加鍵值對. _filter = param_str
        new_query_dict[self.back_condition_key] = param_str
        # 添加url和搜索條件作拼接
        add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
        # 返回最終url
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return edit_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),)

        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return del_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),)

        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

重啓django項目,訪問頁面: http://127.0.0.1:8000/stark/app01/depart/list/

輸入搜索條件 xiao,點擊搜索按鈕

查看添加按鈕的跳轉地址,發下已經添加了搜索條件

 

修改app01-->stark.py,增長編輯和刪除選項

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartModelForm(forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = "__all__"

    def clean_name(self):  # 定義鉤子
        # print(self.cleaned_data['name'])
        return self.cleaned_data['name']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'name', 'tel', 'user',StarkConfig.display_edit_del]
    # model_form_class = DepartModelForm
    # 批量操做
    action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = ['name', 'tel', 'user__username']

    # def get_add_btn(self):  # 返回None,表示不顯示添加按鈕
    #     pass
    # def changelist_view(self, request):  # 重寫changelist_view方法
    #     # 渲染自定義的列表頁面
    #     return render(request,'stark/custom_list.html')
    # def get_urls(self):  # 自定義路由
    #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
    #
    #     urlpatterns = [
    #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
    #     ]
    #     return urlpatterns

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)
View Code

刷新頁面,查看編輯和刪除的herf屬性

發送連接地址,也加上了搜索條件。

點擊編輯按鈕,此時的url地址爲:

http://127.0.0.1:8000/stark/app01/depart/1/change/?_filter=q%3Dxiao

修改一條數據,點擊提交

頁面跳轉地址,此時以前的搜索條件還在!

http://127.0.0.1:8000/stark/app01/depart/list/?q=xiao

效果以下:

若是有分頁功能,這個搜索條件,還要不要?固然要啊!

 

4、自定義分頁

展現數據頁面數據過多,通常會採用分頁處理。

這裏使用的分頁,不是django自帶的分頁器(paginator) ,使用的是自定義分頁類。爲何?雖然使用paginator也能夠完成。可是咱們要作的是,跨框架的組件。即便在flask框架中,也依然能使用!

 

 進入stark應用目錄,建立文件夾utils,它表示工具類。在此文件夾下建立文件pagination.py

注意:分頁跳轉時,它是帶有搜索條件的

這裏沒有使用request.GET.urlencode(),爲了作到通用性。其餘框架也可使用!

"""
分頁組件
"""
from urllib.parse import urlencode

class Pagination(object):
    def __init__(self, current_page, all_count, base_url,query_params, per_page=10, pager_page_count=11):
        """
        分頁初始化
        :param current_page: 當前頁碼
        :param per_page: 每頁顯示數據條數
        :param all_count: 數據庫中總條數
        :param base_url: 基礎URL
        :param query_params: QueryDict對象,內部含全部當前URL的原條件
        :param pager_page_count: 頁面上最多顯示的頁碼數量
        """
        self.base_url = base_url
        try:
            self.current_page = int(current_page)
            if self.current_page <= 0:  # 當前頁碼數不能小於等於0
                raise Exception()
        except Exception as e:
            self.current_page = 1
        self.query_params = query_params
        self.per_page = per_page
        self.all_count = all_count
        self.pager_page_count = pager_page_count
        pager_count, b = divmod(all_count, per_page)
        if b != 0:
            pager_count += 1
        self.pager_count = pager_count

        half_pager_page_count = int(pager_page_count / 2)
        self.half_pager_page_count = half_pager_page_count

    @property
    def start(self):
        """
        數據獲取值起始索引
        :return:
        """
        return (self.current_page - 1) * self.per_page

    @property
    def end(self):
        """
        數據獲取值結束索引
        :return:
        """
        return self.current_page * self.per_page

    def page_html(self):
        """
        生成HTML頁碼
        :return:
        """
        # 若是數據總頁碼pager_count<11 pager_page_count
        if self.pager_count < self.pager_page_count:
            pager_start = 1
            pager_end = self.pager_count
        else:
            # 數據頁碼已經超過11
            # 判斷: 若是當前頁 <= 5 half_pager_page_count
            if self.current_page <= self.half_pager_page_count:
                pager_start = 1
                pager_end = self.pager_page_count
            else:
                # 若是: 當前頁+5 > 總頁碼
                if (self.current_page + self.half_pager_page_count) > self.pager_count:
                    pager_end = self.pager_count
                    pager_start = self.pager_count - self.pager_page_count + 1
                else:
                    pager_start = self.current_page - self.half_pager_page_count
                    pager_end = self.current_page + self.half_pager_page_count

        page_list = []

        if self.current_page <= 1:
            prev = '<li><a href="#">上一頁</a></li>'
        else:
            self.query_params['page'] = self.current_page - 1
            prev = '<li><a href="%s?%s">上一頁</a></li>' % (self.base_url,self.query_params.urlencode())
        page_list.append(prev)
        for i in range(pager_start, pager_end + 1):
            self.query_params['page'] = i
            if self.current_page == i:
                tpl = '<li class="active"><a href="%s?%s">%s</a></li>' % (
                    self.base_url, self.query_params.urlencode(), i,)
            else:
                tpl = '<li><a href="%s?%s">%s</a></li>' % (self.base_url, self.query_params.urlencode(), i,)
            page_list.append(tpl)

        if self.current_page >= self.pager_count:
            nex = '<li><a href="#">下一頁</a></li>'
        else:
            self.query_params['page'] = self.current_page + 1
            nex = '<li><a href="%s?%s">下一頁</a></li>' % (self.base_url, self.query_params.urlencode(),)
        page_list.append(nex)
        page_str = "".join(page_list)
        return page_str
View Code

打開表app01_depart,添加幾條數據

修改app01-->stark.py,增長id顯示

from stark.server.stark import site, StarkConfig
from app01 import models
from django import forms
from django.shortcuts import render
from django.conf.urls import url

class UserInfoConfig(StarkConfig):
    list_display = ['id', 'username']


class DepartModelForm(forms.ModelForm):
    class Meta:
        model = models.Depart
        fields = "__all__"

    def clean_name(self):  # 定義鉤子
        # print(self.cleaned_data['name'])
        return self.cleaned_data['name']

class DepartConfig(StarkConfig):
    list_display = [StarkConfig.display_checkbox,'id','name', 'tel', 'user',StarkConfig.display_edit_del]
    # model_form_class = DepartModelForm
    # 批量操做
    action_list = [StarkConfig.multi_delete,StarkConfig.multi_init]
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = ['name', 'tel', 'user__username']

    # def get_add_btn(self):  # 返回None,表示不顯示添加按鈕
    #     pass
    # def changelist_view(self, request):  # 重寫changelist_view方法
    #     # 渲染自定義的列表頁面
    #     return render(request,'stark/custom_list.html')
    # def get_urls(self):  # 自定義路由
    #     info = self.model_class._meta.app_label, self.model_class._meta.model_name
    #
    #     urlpatterns = [
    #         url(r'^list/$', self.changelist_view, name='%s_%s_changelist' % info),
    #     ]
    #     return urlpatterns

site.register(models.UserInfo, UserInfoConfig)
site.register(models.Depart, DepartConfig)
View Code

訪問url:  http://127.0.0.1:8000/stark/app01/depart/list/

效果以下:

 

修改 stark-->server-->stark.py,處理分頁,並傳入參數page給changelist.html

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site
        # 定義request變量,用於非視圖函數使用。
        # 在wrapper裝飾器中,對這個值從新賦值!
        self.request = None
        # url中的搜索條件,存在字典中。key爲_filter
        self.back_condition_key = "_filter"

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def get_search_condition(self, request):  # 根據關鍵字,組合ORM查詢語句
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR"  # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))

        return search_list, q, con

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        search_list, q, con = self.get_search_condition(request)
        # ##### 處理分頁 #####
        from stark.utils.pagination import Pagination
        # 總條數
        total_count = self.model_class.objects.filter(con).count()
        # 複製GET參數
        query_params = request.GET.copy()
        # 容許編輯
        query_params._mutable = True
        # 使用分頁類Pagination,傳入參數。每頁顯示3條
        page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)

        # 根據排序列表進行排序,以及分頁功能
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]
        ### 批量操做 ###
        action_list = self.get_action_list()
        # 獲取函數名以及text屬性
        action_list = [{'name': func.__name__, 'text': func.text} for func in action_list]
        # print(action_list)
        ### 添加按鈕 ###
        add_btn = self.get_add_btn()  # 添加按鈕返回值,不爲空展現,不然不展現


        list_display = self.list_display  # 定義顯示的列
        header_list = []  # 定義頭部,用來顯示verbose_name
        if list_display:
            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    # 執行函數,默認顯示中文
                    verbose_name = name_or_func(self,header=True)
                else:
                    # 獲取指定字段的verbose_name
                    verbose_name = self.model_class._meta.get_field(name_or_func).verbose_name

                header_list.append(verbose_name)
        else:
            # 若是list_display爲空,添加表名
            header_list.append(self.model_class._meta.model_name)

        body_list = []  # 顯示內容

        for row in queryset:
            # 這裏的row是對象,它表示表裏面的一條數據
            row_list = []  # 展現每一行數據
            if not list_display:  # 若是不在list_display裏面
                # 添加對象
                row_list.append(row)
                body_list.append(row_list)
                continue

            for name_or_func in list_display:
                if isinstance(name_or_func,FunctionType):
                    val = name_or_func(self,row=row)  # 執行函數獲取,傳遞row對象
                else:
                    # 使用反射獲取對象的值
                    val = getattr(row, name_or_func)

                row_list.append(val)

            body_list.append(row_list)

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',{'header_list':header_list,'body_list':body_list,'add_btn':add_btn,'action_list':action_list,'q':q,'search_list':search_list,'page':page})

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self, func):
        @functools.wraps(func)
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
            url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)

        # 獲取當前請求的_filter參數,也就是跳轉以前的搜索條件
        origin_condition = self.request.GET.get(self.back_condition_key)
        if not origin_condition:  # 若是沒有獲取到
            return list_url  # 返回列表頁面

        # 列表地址和搜索條件拼接
        list_url = "%s?%s" % (list_url, origin_condition,)

        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)

        if not self.request.GET:  # 判斷get參數爲空
            return add_url  # 返回原url
        # request.GET的數據類型爲QueryDict
        # 對QueryDict作urlencode編碼
        param_str = self.request.GET.urlencode() # 好比q=xiao&age=20
        # 容許對QueryDict作修改
        new_query_dict = QueryDict(mutable=True)
        # 添加鍵值對. _filter = param_str
        new_query_dict[self.back_condition_key] = param_str
        # 添加url和搜索條件作拼接
        add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
        # 返回最終url
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return edit_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),)

        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return del_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),)

        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

修改 stark-->templates-->stark-->changelist.html,增長分頁標籤

{% extends 'stark/layout.html' %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {#添加按鈕#}
        {% if add_btn %}
            <div style="margin: 5px 0;">
                {{ add_btn }}
            </div>
        {% endif %}
        {#搜索框#}
        {% if search_list %}
            <div style="float: right;">
                <form method="GET" class="form-inline">
                    <div class="form-group">
                        <input class="form-control" type="text" name="q" value="{{ q }}" placeholder="關鍵字搜索">
                        <button class="btn btn-primary" type="submit">
                            <i class="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
                </form>
            </div>
        {% endif %}

        <form class="form-inline" method="post">
            {% csrf_token %}
            {#批量操做#}
            {% if action_list %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min-width: 200px;">
                        <option>請選擇功能</option>
                        {% for item in action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="執行">
                </div>
            {% endif %}
            {#使用table展現數據#}
            <table class="table table-bordered">
                <thead>
                <tr>
                    {% for item in header_list %}
                        <th>{{ item }}</th>
                    {% endfor %}

                </tr>
                </thead>
                <tbody>
                {% for row_list in body_list %}
                    <tr>
                        {% for col in row_list %}
                            <td>{{ col }}</td>
                        {% endfor %}

                    </tr>
                {% endfor %}
                </tbody>
            </table>
            {#分頁展現#}
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    {{ page.page_html|safe }}
                </ul>
            </nav>
        </form>
    </div>



{% endblock %}
View Code

基本測試

重啓django,刷新頁面,效果以下:

 

測試搜索條件

 

5、拆分代碼

上面的 stark-->server-->stark.py,代碼太冗長。不方便擴展功能!

要用面向對象的封裝特性,來作代碼拆分。

首先拆分changelist_view方法的render,它傳了不少參數!代碼太長!

修改stark-->server-->stark.py,添加ChangeList類。將changelist_view中的相關變量移植過來

import functools
from django.conf.urls import url
from django.shortcuts import HttpResponse,render,redirect
from types import FunctionType
from django.utils.safestring import mark_safe
from django.urls import reverse
from django import forms
from django.db.models import Q
from django.http import QueryDict

class ChangeList(object):
    """
    封裝列表頁面須要的全部功能
    """
    def __init__(self,config,queryset,q,search_list,page):
        ### 處理搜索 ###
        self.q = q  # 搜索條件
        self.search_list = search_list  # 搜索字段
        self.page = page  # 分頁
        # 配置參數
        self.config = config
        # 批量操做
        self.action_list = [{'name': func.__name__, 'text': func.text} for func in config.get_action_list()]
        # 添加按鈕
        self.add_btn = config.get_add_btn()
        # ORM執行結果
        self.queryset = queryset
        # 顯示的列
        self.list_display = config.get_list_display()

class StarkConfig(object):
    def __init__(self,model_class,site):
        self.model_class = model_class
        self.site = site
        # 定義request變量,用於非視圖函數使用。
        # 在wrapper裝飾器中,對這個值從新賦值!
        self.request = None
        # url中的搜索條件,存在字典中。key爲_filter
        self.back_condition_key = "_filter"

    def display_checkbox(self,row=None,header=False):  # 顯示覆選框
        if header:
            # 輸出中文
            return "選擇"
        # 注意:這裏要寫row.pk,不能寫row.id。你不能保證每個表的主鍵都是id
        return mark_safe("<input type='checkbox' name='pk' value='%s' />" % row.pk)

    def display_edit(self, row=None, header=False):
        if header:
            return "編輯"

        return mark_safe(
            '<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a>' % self.reverse_edit_url(row))

    def display_del(self, row=None, header=False):
        if header:
            return "刪除"

        return mark_safe(
            '<a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>' % self.reverse_del_url(row))

    def display_edit_del(self, row=None, header=False):
        if header:
            return "操做"
        tpl = """<a href="%s"><i class="fa fa-edit" aria-hidden="true"></i></a></a> |
        <a href="%s"><i class="fa fa-trash-o" aria-hidden="true"></i></a>
        """ % (self.reverse_edit_url(row), self.reverse_del_url(row),)
        return mark_safe(tpl)

    def multi_delete(self, request):  # 批量刪除
        """
        批量刪除的action
        :param request:
        :return:
        """
        pk_list = request.POST.getlist('pk')
        self.model_class.objects.filter(pk__in=pk_list).delete()
        # return HttpResponse('刪除成功')

    multi_delete.text = "批量刪除"  # 添加自定義屬性text

    def multi_init(self,request):  # 批量初始化
        print('批量初始化')

    multi_init.text = "批量初始化"  # 添加自定義屬性text

    order_by = []  # 須要排序的字段,由用戶自定義
    list_display = []  # 定義顯示的列,由用戶自定義
    model_form_class = None  # form組件須要的model_class
    action_list = []  # 批量操做方法
    # 搜索字段,若是是跨表字段,要按照ORM語法來
    search_list = []

    def get_order_by(self):  # 獲取排序列表
        return self.order_by

    def get_list_display(self):  # 獲取顯示的列
        return self.list_display

    def get_add_btn(self):  # 顯示添加按鈕
        return mark_safe('<a href="%s" class="btn btn-success">添加</a>' % self.reverse_add_url())

    def get_model_form_class(self):
        """
        獲取ModelForm類
        :return:
        """
        if self.model_form_class:
            return self.model_form_class

        class AddModelForm(forms.ModelForm):
            class Meta:
                model = self.model_class
                fields = "__all__"

        return AddModelForm

    def get_action_list(self):  # 獲取批量操做方法
        val = []  # 空列表
        # 擴展列表的元素
        val.extend(self.action_list)
        return val

    def get_action_dict(self):  # 獲取匹配操做字典
        val = {}
        for item in self.action_list:
            # 以方法名爲key
            val[item.__name__] = item
        return val

    def get_search_list(self):  # 獲取搜索字段
        val = []
        val.extend(self.search_list)
        return val

    def get_search_condition(self, request):  # 根據關鍵字,組合ORM查詢語句
        search_list = self.get_search_list()  # ['name','tel']
        q = request.GET.get('q', "")  # 搜索條件
        con = Q()
        con.connector = "OR"  # 以OR做爲鏈接符
        if q:  # 判斷條件不爲空
            for field in search_list:
                # 合併條件進行查詢, __contains表示使用like查詢
                con.children.append(('%s__contains' % field, q))

        return search_list, q, con

    def changelist_view(self, request):
        """
        全部URL查看列表頁面
        :param request:
        :return:
        """
        if request.method == 'POST':
            action_name = request.POST.get('action')
            action_dict = self.get_action_dict()
            if action_name not in action_dict:
                return HttpResponse('非法請求')

            response = getattr(self, action_name)(request)
            if response:
                return response

        ### 處理搜索 ###
        search_list, q, con = self.get_search_condition(request)
        # ##### 處理分頁 #####
        from stark.utils.pagination import Pagination
        # 總條數
        total_count = self.model_class.objects.filter(con).count()
        # 複製GET參數
        query_params = request.GET.copy()
        # 容許編輯
        query_params._mutable = True
        # 使用分頁類Pagination,傳入參數。每頁顯示3條
        page = Pagination(request.GET.get('page'), total_count, request.path_info, query_params, per_page=3)

        # 根據排序列表進行排序,以及分頁功能
        queryset = self.model_class.objects.filter(con).order_by(*self.get_order_by())[page.start:page.end]

        cl = ChangeList(self, queryset, q, search_list, page)
        context = {
            'cl': cl
        }

        # 注意:要傳入參數
        return render(request,'stark/changelist.html',context)

    def add_view(self, request):
        """
        全部的添加頁面,都在此方法處理
        使用ModelForm實現
        :param request:
        :return:
        """
        # 添加數據,使用ModelForm
        AddModelForm = self.get_model_form_class()

        if request.method == "GET":
            form = AddModelForm()
            return render(request,'stark/change.html',{'form':form})

        form = AddModelForm(request.POST)  # 接收POST數據
        if form.is_valid():  # 驗證數據
            form.save()  # 自動保存數據
            # 反向生成url,跳轉到列表頁面
            return redirect(self.reverse_list_url())
        # 渲染頁面,此時會保存表單數據
        return render(request, 'stark/change.html', {'form': form})

    def change_view(self, request, pk):
        """
        全部編輯頁面
        :param request:
        :param pk:
        :return:
        """
        # 查看單條數據
        obj = self.model_class.objects.filter(pk=pk).first()
        if not obj:
            return HttpResponse('數據不存在')
        # 獲取model_form類
        ModelFormClass = self.get_model_form_class()
        if request.method == 'GET':
            # instance表示生成默認值
            form = ModelFormClass(instance=obj)
            # 渲染頁面,添加和修改能夠共用一個一個模板文件
            return render(request, 'stark/change.html', {'form': form})
        # instance = obj 表示指定給誰作修改
        form = ModelFormClass(data=request.POST, instance=obj)
        if form.is_valid():
            form.save()  # 修改數據
            # 跳轉到列表頁面
            return redirect(self.reverse_list_url())
        return render(request, 'stark/change.html', {'form': form})

    def delete_view(self, request, pk):
        """
        全部刪除頁面
        :param request:
        :param pk:
        :return:
        """
        if request.method == "GET":
            # cancel_url表示用戶點擊取消時,跳轉到列表頁面
            return render(request, 'stark/delete.html', {'cancel_url': self.reverse_list_url()})
        # 定位單條數據,並刪除!
        self.model_class.objects.filter(pk=pk).delete()
        return redirect(self.reverse_list_url())

    def wrapper(self, func):
        @functools.wraps(func)
        def inner(request, *args, **kwargs):
            self.request = request
            return func(request, *args, **kwargs)

        return inner

    def get_urls(self):
        info = self.model_class._meta.app_label, self.model_class._meta.model_name
        urlpatterns = [
            url(r'^list/$', self.wrapper(self.changelist_view), name='%s_%s_changelist' % info),
            url(r'^add/$', self.wrapper(self.add_view), name='%s_%s_add' % info),
            url(r'^(?P<pk>\d+)/change/', self.wrapper(self.change_view), name='%s_%s_change' % info),
            url(r'^(?P<pk>\d+)/del/', self.wrapper(self.delete_view), name='%s_%s_del' % info),
        ]

        extra = self.extra_url()
        if extra:  # 判斷變量不爲空
            # 擴展路由
            urlpatterns.extend(extra)

        # print(urlpatterns)
        return urlpatterns

    def extra_url(self):  # 額外的路由,由調用者重構
        pass

    def reverse_list_url(self):  # 反向生成訪問列表的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_changelist' % (namespace, app_label, model_name)
        list_url = reverse(name)

        # 獲取當前請求的_filter參數,也就是跳轉以前的搜索條件
        origin_condition = self.request.GET.get(self.back_condition_key)
        if not origin_condition:  # 若是沒有獲取到
            return list_url  # 返回列表頁面

        # 列表地址和搜索條件拼接
        list_url = "%s?%s" % (list_url, origin_condition,)

        return list_url

    def reverse_add_url(self):  # 反向生成添加url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        name = '%s:%s_%s_add' % (namespace, app_label, model_name)
        add_url = reverse(name)

        if not self.request.GET:  # 判斷get參數爲空
            return add_url  # 返回原url
        # request.GET的數據類型爲QueryDict
        # 對QueryDict作urlencode編碼
        param_str = self.request.GET.urlencode() # 好比q=xiao&age=20
        # 容許對QueryDict作修改
        new_query_dict = QueryDict(mutable=True)
        # 添加鍵值對. _filter = param_str
        new_query_dict[self.back_condition_key] = param_str
        # 添加url和搜索條件作拼接
        add_url = "%s?%s" % (add_url, new_query_dict.urlencode(),)
        # 返回最終url
        return add_url

    def reverse_edit_url(self, row):  # 反向生成編輯行內容的url
        app_label = self.model_class._meta.app_label  # app名
        model_name = self.model_class._meta.model_name  # 表名
        namespace = self.site.namespace  # 命名空間
        # 拼接字符串,這裏爲change
        name = '%s:%s_%s_change' % (namespace, app_label, model_name)
        # 反向生成url,傳入參數pk=row.pk
        edit_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return edit_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        edit_url = "%s?%s" % (edit_url, new_query_dict.urlencode(),)

        return edit_url

    def reverse_del_url(self, row):  # 反向生成刪除行內容的url
        app_label = self.model_class._meta.app_label
        model_name = self.model_class._meta.model_name
        namespace = self.site.namespace
        # 注意:這裏爲del
        name = '%s:%s_%s_del' % (namespace, app_label, model_name)
        del_url = reverse(name, kwargs={'pk': row.pk})

        if not self.request.GET:
            return del_url
        param_str = self.request.GET.urlencode()
        new_query_dict = QueryDict(mutable=True)
        new_query_dict[self.back_condition_key] = param_str
        del_url = "%s?%s" % (del_url, new_query_dict.urlencode(),)

        return del_url

    @property
    def urls(self):
        return self.get_urls()

class AdminSite(object):
    def __init__(self):
        self._registry = {}
        self.app_name = 'stark'
        self.namespace = 'stark'

    def register(self,model_class,stark_config=None):
        # not None的結果爲Ture
        if not stark_config:
            # 也就是說,當其餘應用調用register時,若是不指定stark_config參數
            # 那麼必然執行下面這段代碼!
            # stark_config和StarkConfig是等值的!都能實例化
            stark_config = StarkConfig

        # 添加鍵值對,實例化類StarkConfig,傳入參數model_class
        # self指的是AdminSite類
        self._registry[model_class] = stark_config(model_class,self)

        # print(self._registry)  # 打印字典
        """
        {
            app01.models.UserInfo:StarkConfig(app01.models.UserInfo)
            app02.models.Role:RoleConfig(app02.models.Role)
        }
        """

        # for k, v in self._registry.items():
        #     print(k,v)

    def get_urls(self):
        urlpatterns = []

        for k, v in self._registry.items():
            # k=modes.UserInfo,v=StarkConfig(models.UserInfo), # 封裝:model_class=UserInfo,site=site對象
            # k=modes.Role,v=RoleConfig(models.Role)           # 封裝:model_class=Role,site=site對象
            app_label = k._meta.app_label
            model_name = k._meta.model_name
            urlpatterns.append(url(r'^%s/%s/' % (app_label, model_name,), (v.urls, None, None)))

        return urlpatterns

    @property
    def urls(self):
        # 調用get_urls方法
        # self.app_name和self.namespace值是同樣的,都是stark
        return self.get_urls(), self.app_name, self.namespace

site = AdminSite()  # 實例化類
View Code

 

inclusion_tag+yield

列表頁面中的table表格數據,應該使用inclusion_tag+yield

進入stark應用目錄,建立目錄templatetags,目錄名必須是這個!在此目錄新建文件stark.py

from django.template import Library
from types import FunctionType


register = Library()

def header_list(cl):
    """
    表頭
    :param cl:
    :return:
    """
    if cl.list_display:
        for name_or_func in cl.list_display:
            if isinstance(name_or_func, FunctionType):
                verbose_name = name_or_func(cl.config, header=True)
            else:
                verbose_name = cl.config.model_class._meta.get_field(name_or_func).verbose_name
            yield verbose_name
    else:
        yield cl.config.model_class._meta.model_name

def body_list(cl):
    """
    表格內容
    :param cl:
    :return:
    """
    for row in cl.queryset:
        row_list = []
        if not cl.list_display:
            row_list.append(row)
            yield row_list
            continue
        for name_or_func in cl.list_display:
            if isinstance(name_or_func, FunctionType):
                val = name_or_func(cl.config, row=row)
            else:
                val = getattr(row, name_or_func)
            row_list.append(val)
        yield row_list

@register.inclusion_tag('stark/table.html')
def table(cl):

    return {'header_list':header_list(cl),'body_list':body_list(cl)}
View Code

 

修改 stark-->templates-->stark-->custom_list.html,使用inclusion_tag

{% extends 'stark/layout.html' %}
{% load stark %}

{% block content %}
    <h1>列表頁面</h1>
    <div>
        {#添加按鈕#}
        {% if cl.add_btn %}
            <div style="margin: 5px 0;">
                {{ cl.add_btn }}
            </div>
        {% endif %}
        {#搜索框#}
        {% if cl.search_list %}
            <div style="float: right;">
                <form method="GET" class="form-inline">
                    <div class="form-group">
                        <input class="form-control" type="text" name="q" value="{{ cl.q }}" placeholder="關鍵字搜索">
                        <button class="btn btn-primary" type="submit">
                            <i class="fa fa-search" aria-hidden="true"></i>
                        </button>
                    </div>
                </form>
            </div>
        {% endif %}

        <form class="form-inline" method="post">
            {% csrf_token %}
            {#批量操做#}
            {% if cl.action_list %}
                <div class="form-group">
                    <select name="action" class="form-control" style="min-width: 200px;">
                        <option>請選擇功能</option>
                        {% for item in cl.action_list %}
                            <option value="{{ item.name }}">{{ item.text }}</option>
                        {% endfor %}
                    </select>
                    <input class="btn btn-primary" type="submit" value="執行">
                </div>
            {% endif %}
            {#使用table展現數據#}
            {% table cl %}
            {#分頁展現#}
            <nav aria-label="Page navigation">
                <ul class="pagination">
                    {{ cl.page.page_html|safe }}
                </ul>
            </nav>
        </form>
    </div>



{% endblock %}
View Code

務必重啓django,由於必須重啓,inclusion_tag纔會生效!

訪問url: http://127.0.0.1:8000/stark/app01/depart/list

效果以下:

 

總結:

1. 批量操做[擴展]
    - 反射 
    - __name__ 
    - 一切皆對象
        def multi_delete(self,request):
            """
            批量刪除的action
            :param request:
            :return:
            """
            pk_list = request.POST.getlist('pk')
            self.model_class.objects.filter(pk__in=pk_list).delete()
            # return HttpResponse('刪除成功')

        multi_delete.text = "批量刪除"

2. 搜索[擴展]
    - Q 
    - __contains


3. 保留原搜索條件 
    - QueryDict,request.GET/request.POST 
        - urlencode()
        - _mutable = True 
        - 深拷貝 
        - urllib.parse.urlencode

4. 分頁 
    - 分頁組件
    - 保留原條件
    
5. 拆分 
    - ChangeList類封裝 
    - inclusion_tag
    - 生成器 
View Code

 

完整代碼,請參數github:

https://github.com/987334176/luffy_stark/archive/v1.2.zip

相關文章
相關標籤/搜索