【Python之路】特別篇--基於領域驅動模型架構設計的京東用戶管理後臺

1、預備知識:html

一、接口:前端

  - URL形式  python

  - 數據類型 (Python中不存在)mysql

 

a.類中的方法能夠寫任意個,想要對類中的方法進行約束就可使用接口;sql

b.定義一個接口,接口中定義一個方法f1;數據庫

c.只要繼承(實現)了接口,那麼類就會受約束,該類必需要有f1方法!session

d.接口只用來作約束,不須要寫具體功能。app

因爲python中無接口類型,可是能夠人爲構造,拋出異常!框架

raise Exception('子類中必須實現該方法')ide

class IOrderRepository:
    def fetch_one_by(self,nid):
        '''
        獲取單條數據的方法,全部繼承當前類的類必須實現(有)該方法
        :param nid:
        :return:
        '''
        raise Exception('子類中必須實現該方法')

class OrderRepository(IOrderRepository):
    
    def fetch_one_by(self,nid):
        print('...獲取數據')


obj = OrderRepository
obj.fetch_one_by(1)
demo

 

類:類中的方法能夠寫功能,也能夠不寫功能,

抽象類:類和接口的集合,抽象類中能夠有抽象方法和普通方法,

          子類繼承抽象類時,必須實現抽象方法!

           抽象類即作了約束,還有具體功能

python提供了模塊abc,用於實現抽象類和抽象方法的功能

import abc

class Foo(metaclass=abc.ABCMeta):

    def f1(self):
        print(123)

    def f2(self):
        print(456)

    @abc.abstractmethod
    def f3(self):
        '''

        :return:
        '''

class Bar(Foo):
    def f1(self):
        pass

    def f3(self):
        pass


b = Bar()
b.f1()
demo

 


 

耦合:

不一樣模塊之間互連程度的度量(耦合性也叫塊間聯繫)。指軟件系統中各模塊間相互聯繫緊密程度的一種度量。模塊之間聯繫越緊密,其耦合性就越強,模塊的獨立性則越差。

class SqlHelper:
    def fetch_one(self):
        pass
    def fetch_all(self):
        pass

方式一:
class UserInfo:
    def __init__(self):
        pass

    def login(self):
        s = SqlHelper()
        s.fetch_one()

    def logout(self):
        s = SqlHelper()
        s.fetch_one()

    def register(self):
        s = SqlHelper()
        s.fetch_one()

*************************************************
方式二:
class UserInfo:
    def __init__(self,helper):
        self.s = SqlHelper()

    def login(self):
        self.s.fetch_one()

    def logout(self):
        self.s.fetch_one()

    def register(self):
        self.s.fetch_one()    


obj = UserInfo()
obj.login()
強耦合demo

爲了下降耦合,通常能夠這樣作:

class SqlHelper:
    def fetch_one(self):
        pass
    def fetch_all(self):
        pass

class UserInfo:
    def __init__(self,helper):
        self.s = helper

    def login(self):
        self.s.fetch_one()

h = SqlHelper()
obj = UserInfo(h)
obj.login()
demo

若是SqlHelper和其餘類,如Foo類有依賴關係那麼又要解耦了:

class Foo:
    def f1(self):
        pass

class SqlHelper:
    def __init__(self,foo):
        self.f = foo

    def fetch_one(self):
        self.f.f1()

class UserInfo:
    def __init__(self,helper):
        self.s = helper

    def login(self):
        self.s.fetch_one()

f = Foo()
s = SqlHelper(f)
u = UserInfo(s)
u.login()
demo

最終發現代碼,嵌套太多層了。

爲了解決這種問題,可使用依賴注入!

提案:實例化的時候,不須要本身傳參數

 


 

面向對象知識點:

class Foo:
    def __init__(self,name):
        self.name = name

    def f1(self):
        print(self.name)

# 解釋器解釋的時候:

1.遇到 class Foo , 執行type 的__init__方法

2.Typr的init方法裏作什麼呢? => 建立了類

obj = Foo(123)

3.執行Type的__call__方法,

      -> 執行Foo類的__new__方法

      -> 執行Foo類的__init__方法

 

如何在執行__init__方法前,傳遞參數呢?

能夠選擇去重寫Type的__call__方法

你寫的任何類都是Type的對象,

class MyType(type):
    def __call__(self, *args, **kwargs):
        print(self)

class Foo(metaclass=MyType):      # 指定由MyType生成
    def __init__(self,name):
        self.name = name

obj = Foo(123)
# <class '__main__.Foo'>

最終執行流程:

class MyType(type):
 
    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls,*args, **kwargs)  # 建立對象
        obj.__init__(*args, **kwargs)      # 初始化
        return obj
 
class Foo(metaclass=MyType):
    def __init__(self,name):
        self.name = name
 
    def f1(self):
        print(self.name)
 
obj = Foo(123)

爲了在調用時不傳參數,設法在obj.__init__()執行前時,傳入參數

class MyType(type):

    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls,*args, **kwargs)
        args_list = list(args)
        args_list.append(123)
        obj.__init__(*args_list, **kwargs)
        return obj

obj = Foo()

爲了避免讓參數固定,作進一步修改,利用第三方來傳遞參數!

依賴注入:

class Mapper:

    __mapper_relation = {}

    @staticmethod
    def register(cls,value):
        Mapper.__mapper_relation[cls] = value

    @staticmethod
    def exist(cls):
        if cls in  Mapper.__mapper_relation:
            return True
        else:
            return False

    @staticmethod
    def value(cls):
        return Mapper.__mapper_relation[cls]

class MyType(type):

    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls,*args, **kwargs)
        args_list = list(args)
        if Mapper.exist(cls):
            value = Mapper.value(cls)
            args_list.append(value)
        obj.__init__(*args_list, **kwargs)
        return obj

class Foo(metaclass=MyType):
    def __init__(self,name):
        self.name = name

    def f1(self):
        print(self.name)


Mapper.register(Foo,'666999')

obj = Foo()
obj.f1()
demo

有了依賴注入後,以前嵌套過多的demo能夠修改成:

#!/usr/bin/env python
# -*-coding:utf-8 -*-
class Mapper:

    __mapper_relation = {}

    @staticmethod
    def register(cls,value):
        Mapper.__mapper_relation[cls] = value

    @staticmethod
    def exist(cls):
        if cls in  Mapper.__mapper_relation:
            return True
        else:
            return False

    @staticmethod
    def value(cls):
        return Mapper.__mapper_relation[cls]

class MyType(type):

    def __call__(cls, *args, **kwargs):
        obj = cls.__new__(cls,*args, **kwargs)
        args_list = list(args)
        if Mapper.exist(cls):
            value = Mapper.value(cls)
            args_list.append(value)
        obj.__init__(*args_list, **kwargs)
        return obj

class Test():
    def __int__(self):
        pass

class Foo(metaclass=MyType):
    def __init__(self,t):
        self.t = t

    def f1(self):
        print(self.name)

class Bar(metaclass=MyType):
    def __init__(self,f):
        self.f = f

    def f1(self):
        print(self.name)

Mapper.register(Foo,Test())
Mapper.register(Bar,Foo())

b = Bar()
print(b)
print(b.f)
print(b.f.t)

# <__main__.Bar object at 0x0000002D504C4550>
# <__main__.Foo object at 0x0000002D504C4518>
# <__main__.Test object at 0x0000002D504C4470>
View Code

 

1.整體框架目錄結構:

備註:

  • Infrastructure:一些公共組件,例如md5加密,分頁模塊,session等。
  • Model :關於數據庫的邏輯處理模塊
  • Repository :數據訪問層,包含數據庫的增刪改查
  • Service :服務層,調用Model,包含帶有規則的請求和返回
  • Statics:靜態文件目錄
  • UI層:業務處理
  • Views:模板文件
  • Application:tornado程序的起始文件
  • Config:配置文件
  • Mapper:依賴注入文件,負責整個框架不一樣類的依賴注入

2.首先咱們從Moldel開始查看:

本文主要以用戶管理爲例進行介紹,所以咱們來關注一下User.py文件:

代碼結構:

下面對上述代碼結構作一一介紹:

IUseRepository類:接口類,用於約束數據庫訪問類的方法
示例代碼:

class IUseRepository:
    """
    用戶信息倉庫接口
    """
 
    def fetch_one_by_user_pwd(self, username, password):
        """
        根據用戶名密碼獲取模型對象
        :param username: 主鍵ID
        :param password: 主鍵ID
        :return:
        """
    def fetch_one_by_email_pwd(self, email, password):
        """
        根據郵箱密碼獲取模型對象
        :param email: 主鍵ID
        :param password: 主鍵ID
        :return:
        """
 
    def update_last_login_by_nid(self,nid,current_date):
        """
        根據ID更新最新登錄時間
        :param nid:
        :return:
        """

  從上述代碼能夠看出,數據庫訪問類若是繼承IUseRepository類,就必須實現其中的抽象方法。

接下來的三個類,VipType、UserType、User是與用戶信息相關的類,是數據庫須要保存的數據,

咱們但願存入數據庫的數據格式爲:nid 、username、email、last_login、user_type_id、vip_type_id,其中User類用於保存上述數據。

由於user_type_id、vip_type_id存的是數字,即user_type_id、vip_type_id是外鍵,不能直接在前端進行展現,

所以,咱們建立了VipType、UserType類,用於根據id,獲取對應的VIP級別和用戶類型。

class User:
    """領域模型"""
    def __init__(self, nid, username, email, last_login, user_type, vip_type):
        self.nid = nid
        self.username = username
        self.email = email
        self.last_login = last_login
        self.user_type = user_type
        self.vip_type = vip_type
User類
class UserType:

    USER_TYPE = (
        {'nid': 1, 'caption': '用戶'},
        {'nid': 2, 'caption': '商戶'},
        {'nid': 3, 'caption': '管理員'},
    )

    def __init__(self, nid):
        self.nid = nid

    def get_caption(self):
        caption = None

        for item in UserType.USER_TYPE:
            if item['nid'] == self.nid:
                caption = item['caption']
                break
        return caption

    caption = property(get_caption)
UserType類
class VipType:

    VIP_TYPE = (
        {'nid': 1, 'caption': '銅牌'},
        {'nid': 2, 'caption': '銀牌'},
        {'nid': 3, 'caption': '金牌'},
        {'nid': 4, 'caption': '鉑金'},
    )

    def __init__(self, nid):
        self.nid = nid

    def get_caption(self):
        caption = None

        for item in VipType.VIP_TYPE:
            if item['nid'] == self.nid:
                caption = item['caption']
                break
        return caption

    caption = property(get_caption)
VipType類

注:VipType、UserType這兩個類獲取對應的caption均是經過類的普通特性訪問,即相似字段方式訪問。

接下來的類UserService是本py文件的重中之重,它負責調用對應的數據庫訪問類的方法,並被服務層service調用,具備承上啓下的做用:

示例代碼:

class UserService:
 
    def __init__(self, user_repository):
        self.userRepository = user_repository
 
    def check_login(self, username=None, email=None, password=None):
 
        if username:
            user_model = self.userRepository.fetch_one_by_user_pwd(username, password)
        else:
            user_model = self.userRepository.fetch_one_by_email_pwd(email, password)
        if user_model:
            current_date = datetime.datetime.now()
            self.userRepository.update_last_login_by_nid(user_model.nid, current_date)
        return user_model

  這裏,咱們重點介紹一下上述代碼:

初始化參數user_repository:經過依賴注入對應的數據庫訪問類的對象;

check_login:訪問數據庫的關鍵邏輯處理方法,根據用戶是用戶名+密碼方式仍是郵箱加密碼的方式登陸,而後調用對應的數據庫處理方法,若是登錄成功,更新時間和最後登陸時間,最後將User類的實例返回給調用它的服務層service。(詳細見下文數據庫處理類的方法)

咱們先來看一下對應的數據庫處理類中的一個方法:

def fetch_one_by_user_pwd(self, username, password):
       ret = None
       cursor = self.db_conn.connect()
       sql = """select nid,username,email,last_login,vip,user_type from UserInfo where username=%s and password=%s"""
       cursor.execute(sql, (username, password))
       db_result = cursor.fetchone()
       self.db_conn.close()
 
       if db_result:
           ret = User(nid=db_result['nid'],
                      username=db_result['username'],
                      email=db_result['email'],
                      last_login=db_result['last_login'],
                      user_type=UserType(nid=db_result['user_type']),
                      vip_type=VipType(nid=db_result['vip'])
                      )
       return ret

  這裏咱們使用pymysql進行數據庫操做,以用戶名+密碼登錄爲例,若是數據庫有對應的用戶名和密碼,將查詢結果放在User類中進行初始化,至此,ret中封裝了用戶的所有基本信息,將ret返回給上面的check_login方法,即對應上文中的返回值user_model,user_model返回給調用它的服務層service。

總結:Molde最終將封裝了用戶基本信息的User類的實例返回給服務層service。

3.接下來咱們看一下服務層service:

service也是一個承上啓下的做用,它調用Moldel文件對應的數據庫業務協調方法,並被對應的UI層調用(本例中是UIadmin)。

目錄結構:

一樣的,咱們只介紹User文件夾:它包含4個py文件:

  • ModelView:用於用戶前端展現的數據格式化,重點對user_type_id、vip_type_id增長對應的用戶類型和VIP級別,即將外鍵數據對應的caption放在外鍵後面,做爲增長的參數。
  • Request:封裝請求Moldel文件對應數據庫協調類的參數
  • Response:封裝須要返回UI層的參數
  • Sevice:核心服務處理文件

下面對上述目錄作詳細代碼:

ModelView:

class UserModelView:
 
    def __init__(self, nid, username, email, last_login, user_type_id, user_type_caption, vip_type_id, vip_type_caption):
        self.nid = nid
        self.username = username
        self.email = email
        self.last_login = last_login
        self.user_type = user_type_id
        self.user_type_caption = user_type_caption
        self.vip_type = vip_type_id
        self.vip_type_caption = vip_type_caption

  注:對user_type_id、vip_type_id增長對應的用戶類型和VIP級別,即將外鍵數據對應的caption放在外鍵後面,做爲增長的參數。

Request:

class UserRequest:
 
    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = password

Response:

class UserResponse:
 
    def __init__(self, status=True, message='', model_view=None):
        self.status = status    # 是否登錄成功的狀態
        self.message = message  #錯誤信息
        self.modelView = model_view  #登錄成功後的用戶數據 

UserService:

class UserService:
 
    def __init__(self, model_user_service):   #經過依賴注入Moldel對應的數據庫業務協調方法,此例中對應上文中的user_service
        self.modelUserService = model_user_service  
 
    def check_login(self, user_request):  #核心服務層業務處理方法
        response = UserResponse()  #實例化返回類
 
        try:
            model = self.modelUserService.check_login(user_request.username, user_request.email, user_request.password) #接收上文中的用戶基本信息,是User類的實例
            if not model:
                raise Exception('用戶名或密碼錯誤')
            else:   #若是登錄成功,經過UserModelView類格式化返回前端的數據
                model_view = UserModelView(nid=model['nid'],
                                           username=model['usename'],
                                           email=model['email'],
                                           last_login=model['last_login'],
                                           user_type_id=model['user_type'].nid,
                                           user_type_caption=model['user_type'].caption,
                                           vip_type_id=model['vip_type'].nid,
                                           vip_type_caption=model['vip_type'].caption,)
                response.modelView = model_view    #定義返回UI層的用戶信息
        except Exception as e:
            response.status = False
            response.message = str(e)

  總結:至此,Service返回給Ui層的數據是是否登錄成功的狀態status、錯誤信息、已經格式化的用戶基本信息。

4.UI層

UI層主要負責業務處理完成後與前端的交互,它是服務層Service的調用者:

示例代碼:

class Login(AdminRequestHandler):
 
    def get(self, *args, **kwargs):
        is_login=self.session["is_login"]
        current_user=""
        if is_login:
            current_user=self.session["user_info"].username
        self.render('Account/Login.html',current_user=current_user)
 
    def post(self, *args, **kwargs):
        user_request=[]
        #獲取用戶輸入的用戶名郵箱密碼
        user_request.append(self.get_argument('name',None))
        user_request.append(self.get_argument('email',None))
        user_request.append(self.get_argument('pwd',None))
        checkcode=self.get_argument("checkcode",None)
        Mapper.mapper(*user_request)
        obj=UserService.check_login()
        self.session["is_login"]=True
        self.session["user_info"]=obj.modelView
        self.write("已登錄")
相關文章
相關標籤/搜索