先來看下面一段代碼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
官方說法:x稱之爲 類變量 y稱之爲 實例變量java
在java和C#中,分別叫 靜態字段和字段python
獲取函數的名字,可經過 函數.__name__ 方法獲取。git
那麼在django模板中,不能使用雙下方法。會報錯!github
URL編碼(URL encoding),也稱做百分號編碼(Percent-encoding), 是特定上下文的統一資源定位符 (URL)的編碼機制。web
發現如今幾乎全部的網站都對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
怎麼轉換?
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)
執行輸出:k1=v1&k2=v2
from urllib.parse import urlencode info = {'k1':'v1','k2':'v2'} print(urlencode(info))
以上能夠看出,使用urlencode更方便!
字典有多少個key-value,均可以方便的轉換!
在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), ]
修改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')
啓動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')
它內部調用了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')
刷新頁面,效果以下:
提示: 這個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')
重啓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')
重啓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')
刷新頁面,查看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')
重啓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')
刷新頁面,查看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')
刷新頁面,查看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')
刷新頁面,查看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), ]
修改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)
重啓django程序,訪問頁面
注意:這裏直接訪問的是帶有搜索條件的
http://127.0.0.1:8000/index/?name=xiao&age=20
效果以下:
點擊添加學生,頁面跳轉
注意:這裏面的_filter,就是原來的搜索條件
點擊提交按鈕,頁面跳轉
此時頁面:仍是帶有原來的搜索條件
通常頁面使用的搜索功能,都是使用了模糊搜索
好比:查詢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%'
務必下載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 %}
修改 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)
訪問頁面: 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() # 實例化類
修改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)
修改 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 %}
重啓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() # 實例化類
修改 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 %}
刷新頁面,效果以下:
它是英文的,要中文展現,怎麼搞?
python一切皆對象,能夠爲函數添加一個屬性。屬性名無所謂!
新建一個a.py文件,內容以下:
def func(): print(123) func.aa = '批量刪除' print(func.aa)
執行輸出:批量刪除
一樣的道理,給函數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() # 實例化類
重啓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() # 實例化類
修改 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 %}
刷新頁面,效果以下:
能夠看到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 %}
修改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() # 實例化類
刷新頁面,選擇批量刪除,勾選幾條數據。點擊提交
查看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() # 實例化類
刷新頁面,再次選擇批量刪除,勾選幾條數據,點擊提交!
查看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() # 實例化類
刷新頁面,點擊匹配初始化,仍是會跳轉到當前頁面。由於此方法,沒有返回值。
點擊批量刪除,頁面效果以下:
修改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() # 實例化類
刷新頁面,選中一條數據,點擊提交
發現少了一條數據
訪問其它頁面,好比: 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 %}
刷新頁面,效果以下:
多選框,沒有了!
其餘頁面,想使用批量操做,定義action_list變量,就能夠了!
搜索什麼內容,取哪一個字段搜,都是能夠定製的!
修改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() # 實例化類
訪問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() # 實例化類
修改 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 %}
訪問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)
重啓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() # 實例化類
修改 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 %}
修改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() # 實例化類
修改app01-->stark.py,開啓search_list變量
# 搜索字段,若是是跨表字段,要按照ORM語法來 search_list = ['name', 'tel', 'user__username']
重啓django,刷新頁面。測試搜索功能,效果同上!
在內容前戲部分,講到了 保留原搜索條件。關鍵點在於:搜索到結果後,跳轉其餘頁面時,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() # 實例化類
重啓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() # 實例化類
重啓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)
刷新頁面,查看編輯和刪除的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
效果以下:
若是有分頁功能,這個搜索條件,還要不要?固然要啊!
展現數據頁面數據過多,通常會採用分頁處理。
這裏使用的分頁,不是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
打開表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)
訪問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() # 實例化類
修改 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 %}
基本測試
重啓django,刷新頁面,效果以下:
測試搜索條件
上面的 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() # 實例化類
列表頁面中的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)}
修改 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 %}
務必重啓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 - 生成器
完整代碼,請參數github: