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)
類:類中的方法能夠寫功能,也能夠不寫功能,
抽象類:類和接口的集合,抽象類中能夠有抽象方法和普通方法,
子類繼承抽象類時,必須實現抽象方法!
抽象類即作了約束,還有具體功能
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()
耦合:
不一樣模塊之間互連程度的度量(耦合性也叫塊間聯繫)。指軟件系統中各模塊間相互聯繫緊密程度的一種度量。模塊之間聯繫越緊密,其耦合性就越強,模塊的獨立性則越差。
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()
爲了下降耦合,通常能夠這樣作:
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()
若是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()
最終發現代碼,嵌套太多層了。
爲了解決這種問題,可使用依賴注入!
提案:實例化的時候,不須要本身傳參數
面向對象知識點:
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能夠修改成:
#!/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>
1.整體框架目錄結構:
備註:
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
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)
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、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:
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("已登錄")