python 全棧開發,Day116(可迭代對象,type建立動態類,偏函數,面向對象的封裝,獲取外鍵數據,組合搜索,領域驅動設計(DDD))

 

昨日內容回顧

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])  # 添加多個
View Code

 

1、可迭代對象

什麼是可迭代對象

在以前學過的容器中,許多容器都是可迭代對象,能夠直接用於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

 

舉例1

看下面一段代碼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

 

舉例2

這是汽車之家的篩選頁面,每個行的數據是循環展現的!如何構造這些數據?

方法一:使用字典

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
汽油
柴油
混合動力
電動
View Code

 

方法二:使用對象+yield

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

執行輸出,效果同上!

 

爲何要用對象構造數據?由於要構造更復雜的數據結構!

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

執行輸出:

<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>
View Code

能夠看出,這段數據,比上面的複雜!

若是用列表,那麼有不少if判斷!使用類,2層for循環就出來了!

爲何要輸出a標籤?由於前端渲染麻煩,後端生成a標籤。前端容易展現,還能夠作一些複雜的需求!

 

2、type建立動態類

前戲

舉例1

def gen_cls():
    class Foo(object):
        pass

    return Foo

cls = gen_cls()

print(cls)
View Code

執行輸出:

<class '__main__.gen_cls.<locals>.Foo'>

對象是由類建立的。那麼類是由誰建立的?先帶着這個疑問

舉例2

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還有一種徹底不一樣的功能,動態的建立類。

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

執行輸出:

<__main__.Foo object at 0x000001E0FBF2DA20>
中國
101

根據條件,由type動態建立了一個類。類的基本操做,好比實例化,獲取靜態字段,傳參,都沒有問題!

 

函數type其實是一個元類。type就是Python在背後用來建立全部類的元類。type就是Python的內建元類

總結:對象是由類建立的。那麼類默認是由type建立的

 

3、偏函數

什麼是偏函數

偏函數是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中,會用到偏函數

 

4、面向對象的封裝

看下面一段數據

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__)
View Code

執行輸出:

{'name': '全智賢', 'color': 'pink', 'sex': ''}
{'name': '高圓圓', 'color': 'red', 'sex': ''}
{'name': '胡歌', 'color': 'black', 'sex': ''}

經過這2種方法對比,使用類更好一點!

若是使用字典,那麼第一條數據,加了一個參數。而另外2條數據,卻沒有加!

那麼頁面展現就會錯亂!

而使用類,則不會這個狀況!另一點,使用類,能夠構造更加複雜的數據結構!

使用類,是新的 封裝思想。可擴展功能比較方便

 

5、獲取外鍵數據

外鍵分爲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
View Code

使用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');
View Code

 

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

 

修改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('...')
View Code

啓動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來獲取的

 

6、組合搜索

展現頁面

務必下載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 %}
View Code

訪問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()  # 實例化類
View Code

 

修改 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)
View Code

刷新頁面,查看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()  # 實例化類
View Code

修改 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 %}
View Code

刷新頁面,效果以下:

 

 

 

 

 

 

後臺搜索

 

關於後面的詳細步驟,沒有時間寫了。附上完整代碼:

連接:https://pan.baidu.com/s/1fLOGH_3G7hPTvCYKX84UdQ 密碼:m8rh

 

7、領域驅動設計(DDD)

什麼是領域驅動設計(DDD)

2004年著名建模專家Eric Evans發表了他最具影響力的書籍:《Domain-Driven Design –Tackling Complexity in the Heart of Software》(中文譯名:領域驅動設計—軟件核心複雜性應對之道),書中提出了「領域驅動設計(簡稱 DDD)」的概念。

  領域驅動設計事實上是針對OOAD的一個擴展和延伸,DDD基於面向對象分析與設計技術,對技術架構進行了分層規劃,同時對每一個類進行了策略和類型的劃分。

  領域模型是領域驅動的核心。採用DDD的設計思想,業務邏輯再也不集中在幾個大型的類上,而是由大量相對小的領域對象(類)組成,這些類具有本身的狀態和行爲,每一個類是相對完整的獨立體,並與現實領域的業務對象映射。領域模型就是由這樣許多的細粒度的類組成。基於領域驅動的設計,保證了系統的可維護性、擴展性和複用性,在處理複雜業務邏輯方面有着先天的優點。

領域驅動設計的特色

領域驅動的核心應用場景就是解決複雜業務的設計問題,其特色與這一核心主題息息相關:

  1. 分層架構與職責劃分:領域驅動設計很好的遵循了關注點分離的原則,提出了成熟、清晰的分層架構。同時對領域對象進行了明確的策略和職責劃分,讓領域對象和現實世界中的業務造成良好的映射關係,爲領域專家與開發人員搭建了溝通的橋樑。
  2. 複用:在領域驅動設計中,領域對象是核心,每一個領域對象都是一個相對完整的內聚的業務對象描述,因此能夠造成直接的複用。同時設計過程是基於領域對象而不是基於數據庫的Schema,因此整個設計也是能夠複用的。
  3. 使用場景:適合具有複雜業務邏輯的軟件系統,對軟件的可維護性和擴展性要求比較高。不適用簡單的增刪改查業務。

 

舉例:商品價格

一個商品,有商品名(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
View Code

重點就是建模
通常作3年開發,就能夠領悟 領域驅動設計。具體還得看我的的領悟能力!

 

其餘更多信息,請參考連接:

http://www.javashuo.com/article/p-pkqcxalf-es.html

 

關於python方面領域驅動設計的相關書籍,暫時尚未。

主流的是JAVA,C#,PHP

 

領域驅動設計它是一種編程思想,重點就是建模!對於開發一個大型項目,尤其重要!

在python源代碼中,就利用這種思想。類中層層嵌套類!

對於我的編程能力的提高,能夠看一下相關書籍!

 

未完待續...

相關文章
相關標籤/搜索