1. 三個類 ChangeList,封裝列表頁面須要的全部數據。 StarkConfig,生成URL和視圖對應關係 + 默認配置 AdminSite,用於保存 數據庫類 和 處理該類的對象 的對應關係 + 路由分發 _registry = { } 2. 知識點 inclusion_tag yield urlencode _meta.model_name _meta.app_label 深淺拷貝 QueryDict對象默認不可改 _mutable=True 生成器 路由分發: - include - ([],None,None) 函數和方法的區別? Q的做用?構造複雜的查詢條件 models.User.object.filter(name__contains='李') models.User.object.filter(name__contains='李',email__contains='李') 構造 or c1 = Q() c1.connector = 'OR' c1.children.append( ('name__contains','李') ) c1.children.append( ('email__contains','李') ) c2 = Q() c2.connector = 'ADN' c2.children.append( ('id__gt',2) ) c3.children.append( ('age__lte',5) ) c3 = Q() c3.connector = 'ADN' c3.add(c1,"ADN") c3.add(c2,"ADN") 等同於 (name=li or email = li) AND ( id>2 and age<=5) models.User.object.filter(con) 反射 有2個地方用到了反射 list_display: # 顯示指定的列 row.name getattr(row,'name') # 批量操做 response = getattr(self, action_name)(request) pass 繼承 class RoleConfig(StarkConfig): pass self究竟是誰? 反向生成URL reverse('xxx') reverse('namespace:xxx') 多個namespace,也是以冒號分隔 分頁(保留原搜索條件) 跳轉的url中保留_filter=原搜索條件 ModelForm組件 添加和修改 functools - wraps,用於保留原函數的元信息(函數名/函數註釋等信息) - partial,偏函數爲函數默認傳參。 import functools def func(a1,a2): print(a1+a2) new_func = functools.partial(func,8) new_func(7) new_func(2) new_func(8) 預留可擴展位置 request.GET request.GET.get('x') request.GET['x'] request.GET.getlist('xxx') request.GET._mutable = True request.GET.copy() request.GET.urlencode() mark_safe xss攻擊是什麼? 跨站腳本攻擊 通常用js代碼,進行攻擊。 獲取cookie,模擬登陸,作非法操做! 單例模式 - 多個模塊之間導入 - 共享同一個數據時 獲取函數名 __name__ autodiscover_module 自動發現模塊 裝飾器 添加request參數,方便非視圖函數調用request order_by 排序展現 __str__ 在models.py中使用 3. QueryDict對象 params = request.GET.copy() # 使用深copy params._mutable = True # 容許修改 params['k1'] = 'v1' # 添加單個 params.setlist('k2',[11,22]) # 添加多個
在以前學過的容器中,許多容器都是可迭代對象,能夠直接用於for…in…循環的而對象都是可迭代對象,好比:list,tuple,dict,set,str等等。css
可迭代對象知足條件:實現了__iter__方法html
注意:__iter__方法必須返回一個迭代器(iter)前端
可迭代對象並非一種具體的數據類型,好比list是可迭代對象,dict也是可迭代對象。python
如何判斷一個對象是不是可迭代對象? 使用isinstance()函數git
from collections import Iterable a = isinstance("123",Iterable) b = isinstance(1,Iterable) print(a) #字符串是否是可迭代對象 返回True print(b) #數字是否是可迭代對象 返回False
看下面一段代碼github
class Foo(object):
pass obj = Foo() for item in obj: print(item)
執行報錯:sql
TypeError: 'Foo' object is not iterable
提示不可迭代,怎麼讓它能夠迭代?數據庫
先來看可迭代對象知足條件:實現了__iter__方法
django
那麼在類中,定義一個__iter__方法,返回迭代器就能夠了!編程
class Foo(object):
def __iter__(self): # return iter([11,22,33,44]) # 返回迭代器 yield 11 # 返回生成器(迭代器的一種) obj = Foo() for item in obj: print(item)
執行輸出: 11
這是汽車之家的篩選頁面,每個行的數據是循環展現的!如何構造這些數據?
data_list= [ ['1.0如下','1.1-1.6'], ['汽油','柴油','混合動力','電動'], ] for row in data_list: for field in row: print(field)
執行輸出:
1.0如下 1.1-1.6 汽油 柴油 混合動力 電動
class Row(object): def __init__(self,data): self.data = data def __iter__(self): for item in self.data: yield item data_list= [ Row(['1.0如下','1.1-1.6']), Row(['汽油','柴油','混合動力','電動']), ] for row in data_list: for field in row: print(field)
執行輸出,效果同上!
爲何要用對象構造數據?由於要構造更復雜的數據結構!
class Row(object): def __init__(self,data): self.data = data def __iter__(self): yield "<div>" yield '所有' for item in self.data: yield "<a href='/index/?p1=1.0'>%s</a>" %item yield "</div>" data_list= [ Row(['1.0如下','1.1-1.6']), Row(['汽油','柴油','混合動力','電動']), ] for row in data_list: for field in row: print(field)
執行輸出:
<div> 所有 <a href='/index/?p1=1.0'>1.0如下</a> <a href='/index/?p1=1.0'>1.1-1.6</a> </div> <div> 所有 <a href='/index/?p1=1.0'>汽油</a> <a href='/index/?p1=1.0'>柴油</a> <a href='/index/?p1=1.0'>混合動力</a> <a href='/index/?p1=1.0'>電動</a> </div>
能夠看出,這段數據,比上面的複雜!
若是用列表,那麼有不少if判斷!使用類,2層for循環就出來了!
爲何要輸出a標籤?由於前端渲染麻煩,後端生成a標籤。前端容易展現,還能夠作一些複雜的需求!
def gen_cls(): class Foo(object): pass return Foo cls = gen_cls() print(cls)
執行輸出:
<class '__main__.gen_cls.<locals>.Foo'>
對象是由類建立的。那麼類是由誰建立的?先帶着這個疑問
name = "Foo" country = "中國" detail = lambda self, x: x + 1
根據以上三個參數建立一個類,類中有兩個成員。實現的效果以下:
class Foo(object): country = '中國' def detail(self,x): return x + 1
如何用代碼實現呢?
不能這麼寫
class name(object): country = '中國' ...
那麼class名就是name了,須要使用type
type還有一種徹底不一樣的功能,動態的建立類。
type能夠接受一個類的描述做爲參數,而後返回一個類。(要知道,根據傳入參數的不一樣,同一個函數擁有兩種徹底不一樣的用法是一件很傻的事情,但這在Python中是爲了保持向後兼容性)
type能夠像這樣工做:
type(類名,由父類名稱組成的元組(針對繼承的狀況,能夠爲空),包含屬性的字典(名稱和值))
根據上面的3個條件,使用type建立一個類
name = "Foo" country = "中國" detail = lambda self, x: x + 1 # 使用type建立動態類 cls = type(name, (object,), {'country': '中國', 'detail': lambda self, x: x + 1}) obj = cls() print(obj) print(obj.country) print(obj.detail(100))
執行輸出:
<__main__.Foo object at 0x000001E0FBF2DA20> 中國 101
根據條件,由type動態建立了一個類。類的基本操做,好比實例化,獲取靜態字段,傳參,都沒有問題!
函數type其實是一個元類。type就是Python在背後用來建立全部類的元類。type就是Python的內建元類
總結:對象是由類建立的。那麼類默認是由type建立的
偏函數是2.5版本之後引進來的東西。屬於函數式編程的一部分,使用偏函數能夠經過有效地"凍結"那些預先肯定的參數,來緩存函數參數,而後在運行時,當得到須要的剩餘參數後,能夠將他們解凍,傳遞到最終的參數中,從而使用最終肯定的全部參數去調用函數。
def func(a1,a2): return a1+a2 res = func(1,3) print(res)
執行輸出:4
import functools def func(a1,a2): return a1+a2 # 至關於給第一個參數,設置默認參數8 new_func = functools.partial(func,8) # 傳入第二個參數 res = new_func(7) print(res)
執行輸出:15
func有2個參數,怎麼肯定是給第一個參數,設置了默認參數?
import functools def func(a1,a2=22): return a1+a2 # 至關於給第一個參數,設置默認參數8 new_func = functools.partial(func,8) # 傳入第二個參數 res = new_func(7) print(res)
執行輸出:15
注意:a1直接設置默認參數,是會報錯的!默認參數必須在位置參數的後面!
在flask中,會用到偏函數
看下面一段數據
list_filter = ['全智賢','高圓圓','胡歌',]
如何用代碼區分性別?
list_filter = [ {'name':'全智賢','sex':'女'}, {'name':'高圓圓','sex':'女'}, {'name':'胡歌','sex':'男'}, ]
再區分衣服的顏色呢?
list_filter = [ {'name':'全智賢','sex':'女','color':'pink'}, {'name':'高圓圓','sex':'女','color':'red'}, {'name':'胡歌','sex':'男','color':'black'}, ]
若是還有其它屬性呢?繼續加?
使用字典也是一個封裝思想
class Option(object): def __init__(self,name,sex,color): self.name = name self.sex = sex self.color = color list_filter = [ # 字典對象作封裝 Option(name='全智賢',sex='女',color = 'pink'), Option(name='高圓圓',sex='女',color = 'red'), Option(name='胡歌',sex='男',color = 'black'), ] for item in list_filter: print(item.__dict__)
執行輸出:
{'name': '全智賢', 'color': 'pink', 'sex': '女'} {'name': '高圓圓', 'color': 'red', 'sex': '女'} {'name': '胡歌', 'color': 'black', 'sex': '男'}
經過這2種方法對比,使用類更好一點!
若是使用字典,那麼第一條數據,加了一個參數。而另外2條數據,卻沒有加!
那麼頁面展現就會錯亂!
而使用類,則不會這個狀況!另一點,使用類,能夠構造更加複雜的數據結構!
使用類,是新的 封裝思想。可擴展功能比較方便
外鍵分爲3種: FK/M2M/O2O,分別對應一對多,多對多,一對一
給定2個參數
model_class = Depart # 模型表類 _field = "user" # 外鍵字段
要獲取外鍵字段對應的全部數據,如何獲取?
新建項目untitled4,注意:django版本爲1.11
修改models.py,新建2個表
from django.db import models # Create your models here. class UserInfo(models.Model): title = models.CharField(verbose_name='標題',max_length=32) def __str__(self): return self.title class Depart(models.Model): name = models.CharField(verbose_name='部門名稱',max_length=32) tel = models.CharField(verbose_name='聯繫電話',max_length=32) user = models.ForeignKey(verbose_name='負責人',to='UserInfo') def __str__(self): return self.name
使用2個命令生成表
python manage.py makemigrations
python manage.py migrate
增長數據,使用Navicat打開sqlite3數據庫,執行sql語句
INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('總經理', 23456342, 1); INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('技術部', 34565422, 1); INSERT INTO "app01_depart" ("name", "tel", "user_id") VALUES ('運營部', 34344523, 2); INSERT INTO "app01_userinfo" ("title") VALUES ('xiao'); INSERT INTO "app01_userinfo" ("title") VALUES ('zhang');
修改urls.py,增長路徑
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,增長視圖函數
from django.shortcuts import render,HttpResponse from app01 import models # Create your views here. def index(request): # 外鍵對象,經過get_field獲取 fk_obj = models.Depart._meta.get_field("user") print(fk_obj,type(fk_obj)) # .all表示獲取全部數據 user_info_queryset = fk_obj.rel.model.objects.all() print(user_info_queryset) for i in user_info_queryset: print('row:',i) # 打印每個行的數據 return HttpResponse('...')
啓動django項目,訪問首頁
查看Pycharm控制檯輸出:
app01.Depart.user <class 'django.db.models.fields.related.ForeignKey'> <QuerySet [<UserInfo: xiao>, <UserInfo: zhang>]> row: xiao row: zhang
注意:FK/M2M/O2O都是經過get_field來獲取的
務必下載github代碼:
https://github.com/987334176/luffy_stark/archive/v1.2.zip
由於下面的內容,都是這份代碼來修改的!
修改 stark-->templates-->stark-->changelist.html
{% extends 'stark/layout.html' %} {% load stark %} {% block css %} <style> .comb-search { padding: 5px 20px; } .comb-search .row .whole { width: 60px; float: left; } .comb-search .row .others { padding-left: 60px; } .comb-search .row a { display: inline-block; padding: 5px 8px; margin: 3px; border: 1px solid #d4d4d4; } .comb-search .row a { display: inline-block; padding: 5px 8px; margin: 3px; border: 1px solid #d4d4d4; } .comb-search a.active { color: #fff; background-color: #337ab7; border-color: #2e6da4; } </style> {% endblock %} {% block content %} <div> {#組合搜索#} <div class="comb-search"> <div class="row"> <div class="whole"> <a href="#">所有</a> </div> <div class="others"> <a href="#">條件1</a> <a href="#">條件2</a> <a href="#">條件3</a> </div> </div> </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 %}
訪問url: http://127.0.0.1:8000/stark/app01/depart/list/
效果以下:
修改 stark-->server-->stark.py,增長變量 list_filter,增長一個鉤子函數 get_list_filter
使用get_field獲取字段
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 = [] list_filter = [] # 組合搜索 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 get_list_filter(self): # 獲取組合搜索條件 val = [] val.extend(self.list_filter) 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 ### 處理搜索 ### 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) # ######## 組合搜索 ######### # list_filter = ['name','user'] list_filter = self.get_list_filter() for field in list_filter: # 若是field = "name" --> 查Depart全部數據 # 若是field = "user" --> 查UserInfo全部數據 _field = self.model_class._meta.get_field(field) print(_field,type(_field)) # 打印字段類型 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() # 實例化類
修改 app01-->stark.py,增長list_filter屬性
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'] list_filter = ["name","user"] # 組合搜索 # 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)
刷新頁面,查看Pycharm控制檯輸出:
app01.Depart.name <class 'django.db.models.fields.CharField'> app01.Depart.user <class 'django.db.models.fields.related.ForeignKey'>
ForeignKey表示一對多
修改 stark-->server-->stark.py,導入模塊ForeignKey,判斷類型。若是是FK,就跨表查詢。
添加list_filter_rows列表,並傳給模板
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 from django.db.models.fields.related import ForeignKey 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 = [] list_filter = [] # 組合搜索 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 get_list_filter(self): # 獲取組合搜索條件 val = [] val.extend(self.list_filter) 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 ### 處理搜索 ### 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) # ######## 組合搜索 ######### # list_filter = ['name','user'] list_filter = self.get_list_filter() list_filter_rows = [] for field in list_filter: # 若是field = "name" --> 查Depart全部數據 # 若是field = "user" --> 查UserInfo全部數據 _field = self.model_class._meta.get_field(field) # print(_field,type(_field)) # 打印字段類型 if isinstance(_field,ForeignKey): row = _field.rel.model.objects.all() else: row = self.model_class.objects.all() list_filter_rows.append(row) context = { 'cl': cl, 'list_filter_rows':list_filter_rows } # 注意:要傳入參數 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() # 實例化類
修改 stark-->templates-->stark-->changelist.html,for循環list_filter_rows列表
{% extends 'stark/layout.html' %} {% load stark %} {% block css %} <style> .comb-search { padding: 5px 20px; } .comb-search .row .whole { width: 60px; float: left; } .comb-search .row .others { padding-left: 60px; } .comb-search .row a { display: inline-block; padding: 5px 8px; margin: 3px; border: 1px solid #d4d4d4; } .comb-search .row a { display: inline-block; padding: 5px 8px; margin: 3px; border: 1px solid #d4d4d4; } .comb-search a.active { color: #fff; background-color: #337ab7; border-color: #2e6da4; } </style> {% endblock %} {% block content %} <div> {#組合搜索#} <div class="comb-search"> {% for row in list_filter_rows %} <div class="row"> <div class="whole"> <a href="#">所有</a> </div> <div class="others"> {% for obj in row %} <a href="#">{{ obj }}</a> {% endfor %} </div> </div> {% endfor %} </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 %}
刷新頁面,效果以下:
關於後面的詳細步驟,沒有時間寫了。附上完整代碼:
連接:https://pan.baidu.com/s/1fLOGH_3G7hPTvCYKX84UdQ 密碼:m8rh
2004年著名建模專家Eric Evans發表了他最具影響力的書籍:《Domain-Driven Design –Tackling Complexity in the Heart of Software》(中文譯名:領域驅動設計—軟件核心複雜性應對之道),書中提出了「領域驅動設計(簡稱 DDD)」的概念。
領域驅動設計事實上是針對OOAD的一個擴展和延伸,DDD基於面向對象分析與設計技術,對技術架構進行了分層規劃,同時對每一個類進行了策略和類型的劃分。
領域模型是領域驅動的核心。採用DDD的設計思想,業務邏輯再也不集中在幾個大型的類上,而是由大量相對小的領域對象(類)組成,這些類具有本身的狀態和行爲,每一個類是相對完整的獨立體,並與現實領域的業務對象映射。領域模型就是由這樣許多的細粒度的類組成。基於領域驅動的設計,保證了系統的可維護性、擴展性和複用性,在處理複雜業務邏輯方面有着先天的優點。
領域驅動的核心應用場景就是解決複雜業務的設計問題,其特色與這一核心主題息息相關:
一個商品,有商品名(name),原價(price),折扣價(discount)。如何用類來表示呢?
class Goods(object): def __init__(self,name,price,discount): self.name = name self.price = price self.discount = discount
若是要增長優惠券(滿減,立減,折扣),怎麼辦?
class BaseCoupon(object): """ 優惠券基礎類 """ pass class Coupon1(BaseCoupon): """ 滿減 """ pass class Coupon2(BaseCoupon): """ 立減 """ pass class Coupon3(BaseCoupon): """ 折扣 """ pass class Price(object): """ 商品價格 """ def __init__(self,price,discount): self.price = price self.discount = discount def pay(self): # 交易價格 pass class Goods(object): def __init__(self,name): self.name = name
重點就是建模
通常作3年開發,就能夠領悟 領域驅動設計。具體還得看我的的領悟能力!
其餘更多信息,請參考連接:
http://www.javashuo.com/article/p-pkqcxalf-es.html
關於python方面領域驅動設計的相關書籍,暫時尚未。
主流的是JAVA,C#,PHP
領域驅動設計它是一種編程思想,重點就是建模!對於開發一個大型項目,尤其重要!
在python源代碼中,就利用這種思想。類中層層嵌套類!
對於我的編程能力的提高,能夠看一下相關書籍!
未完待續...