爲你的Django APP 寫一層 DAO

若是沒有良好的分層,那麼一個Web項目最終會走向崩潰。java

原因

Django項目,通常是按照 APP 切分的,而且每個 APP 有類似的結構,你們都是『各自管好本身分內的事情』,很有點像微服務的味道。可是許多人寫Django 的代碼,沒有必定的章法,一千我的一千種風格。甚至於,在Controller層出現直接裸調用UserModel.objects.filter的狀況也很多見。然而,咱們發現,針對數據庫的操做,不少都是通用的,這時候,單獨抽取出一層,就顯得頗有必要了。python

參考的對象

如何組織、設計咱們的這個層呢?咱們沒有必要本身絞盡腦汁閉門造車,能夠參考成熟項目的作法。Java Spring 是我參考的對象,通常的Spring 項目,有着很明確分層結構,雖然初期須要寫較多的代碼,可是給後期的代碼維護,着實帶來了不少便利。數據庫

通常會分爲以下層級:bash

Controller
Service
Repository( DAO )
(Mapper,可選,若是使用了Mybatis的話)
Model
複製代碼

結合Django的特性,咱們發現Django的Manager層(即:XXModel.objects),實際上是對應着 DAO 層的,只不過你們的叫法不一樣。app

咱們不妨將抽取的單獨層,叫作DAO 好了,後面咱們也會看到,它其實就是對 Manager 層的API進行組合,對上提供一些通用的操做。微服務

如何寫

在正式寫以前,咱們能夠先根據實際經驗,思考:應該提供哪些通用的API?下面是我根據本身的經驗,得出的結論:spa

  1. save(obj)
  2. delete(obj)
  3. update(obj)
  4. findOne/findAll

那麼經過什麼手段實現呢?得益於 Python 強大的語言特性,讓咱們的代碼能夠沒必要寫得像 Java 那樣冗長乏味。個人步驟以下:設計

  1. 首先封裝一個基礎父類,把全部通用操做,都放置到它裏面,就叫作 BaseDAO 吧。
  2. 其他人,繼承這個父類。
  3. 在 Controller或者Service層使用

下面是代碼片斷:code

# 基於 Python 3.5 的代碼, 若是想要放到 Python 2 中的同窗, 能夠去掉 Type Hint


from .BaseModel import BaseModel   # 通常的項目, 都會封裝一個基類Model


class BaseDAO:
    # 子類必須覆蓋這個
    MODEL_CLASS = BaseModel
    SAVE_BATCH_SIZE = 1000

    def save(self, obj):
        """insert one :param obj: :return: """
        if not obj:
            return False

        obj.save()

        return True

    def save_batch(self, objs, *, batch_size=SAVE_BATCH_SIZE):
        """insert batch :type objs: list[BaseModel] :param objs: :return: """
        if not objs:
            return False

        self.MODEL_CLASS.objects.bulk_create(objs, batch_size=batch_size)

        return True

    def delete(self, obj):
        if not obj:
            return False

        obj.delete()

        return True

    def delete_batch(self, objs):
        if not objs:
            return False

        for obj in objs:
            self.delete(obj)

        return True

    def delete_batch_by_query(self, filter_kw: dict, exclude_kw: dict):
        """批量刪除 """
        self.MODEL_CLASS.objects.filter(**filter_kw).exclude(**exclude_kw).delete()

        return True

    def delete_by_fake(self, obj):
        """假刪除/僞刪除 """
        if obj is None:
            return False

        obj.is_deleted = True

        obj.save()

        return True

    def update(self, obj):
        if not obj:
            return False

        obj.save()

        return True

    def update_batch(self, objs):
        if not objs:
            return False

        for obj in objs:
            self.update(obj)

        return True

    def update_batch_by_query(self, query_kwargs: dict, exclude_kw: dict, newattrs_kwargs: dict):

        self.MODEL_CLASS.objects.filter(**query_kwargs).exclude(**exclude_kw).update(**newattrs_kwargs)

    def find_one(self, filter_kw: dict, exclude_kw: dict, order_bys: list):
        """ :param query_kwargs: :rtype: BaseModel | None :return: """
        qs = self.MODEL_CLASS.objects.filter(**filter_kw).exclude(**exclude_kw)
        if order_bys:
            qs = qs.order_by(*order_bys)

        return qs.first()

    def find_queryset(self, filter_kw: dict, exclude_kw: dict, order_bys: list):
        """ :param filter_kw: :return: """

        return self.MODEL_CLASS.objects.filter(**filter_kw).exclude(**exclude_kw)

    def find_all_model_objs(self, filter_kw: dict, exclude_kw: dict, order_bys: list) -> list:
        return self.find_queryset(filter_kw, exclude_kw, order_bys).all()

    def is_exists(self, filter_kw:dict, exclude_kw:dict) -> bool:
        return self.MODEL_CLASS.objects.filter(**filter_kw).exclude(**exclude_kw).exists()

    def get_count(self, filter_kw:dict, exclude_kw:dict) -> int:
        return self.MODEL_CLASS.objects.filter(**filter_kw).exclude(**exclude_kw).count()
複製代碼

如何使用

好比在某個 Django APP 中使用:對象

某個Django APP, 這裏是 goods
goods/
	views.py
	tests.py
	dao/      ( 也能夠單獨放到一個 dao.py 中, 看本身喜愛.  我比較喜歡弄一個目錄, 而且每個py 文件一個class, 這裏保持和java同樣的風格)
		GoodsDao.py
	models.py
	
	

GoodsDao.py內容
from ..models import Goods
from common_base import BaseDAO

class GoodsDao(BaseDAO):
    MODEL_CLASS = Goods    

複製代碼

上層使用:基本能夠很自由的使用。都是一些通用的CURD 操做,變化不大,而且不再用寫冗長的XXModel.objects.filter

延伸

經過上面總結,咱們能夠看到,確實帶來了一個良好的封裝,雖然初期須要多寫一些代碼,可是後期代碼維護比較舒服。另一個問題是:是否是就該摒棄Goods.objects.filter 這種寫法呢?

我以爲不是的,Goods.objects.filter 仍然能夠自由使用,只不過在 DAO 沒法應對的狀況下(你又懶得再封裝了,由於是低頻操做),就該輪到它出場了。它們二者應該是互爲補充,互相融合,各自都有本身的使用場景。原始的寫法適用於『比較低頻、臨時的CURD操做』,DAO則適用於『比較高頻、通用的CURD操做』。

另外,Python 世界流行的 ORM ,不僅有 Django ORM。好比SQLAlchemy等,你也能夠封裝出一樣相似的 DAO 層,讓本身的代碼越寫越舒服。

相關文章
相關標籤/搜索