为你的Django APP 写一层 DAO

栏目: Python · 发布时间: 7年前

内容简介:Django项目,一般是按照 APP 切分的,并且每一个 APP 有相似的结构,大家都是『各自管好自己份内的事情』,颇有点像微服务的味道。但是许多人写Django 的代码,没有一定的章法,一千个人一千种风格。甚至于,在Controller层出现直接裸调用如何组织、设计我们的这个层呢?我们没有必要自己绞尽脑汁闭门造车,可以参考成熟项目的做法。Java Spring 是我参考的对象,一般的Spring 项目,有着很明确分层结构,虽然初期需要写较多的代码,但是给后期的代码维护,着实带来了很多便利。一般会分为如下

Django项目,一般是按照 APP 切分的,并且每一个 APP 有相似的结构,大家都是『各自管好自己份内的事情』,颇有点像微服务的味道。但是许多人写Django 的代码,没有一定的章法,一千个人一千种风格。甚至于,在Controller层出现直接裸调用 UserModel.objects.filter 的情况也不少见。然而,我们发现,针对数据库的操作,很多都是通用的,这时候,单独抽取出一层,就显得很有必要了。

参考的对象

如何组织、设计我们的这个层呢?我们没有必要自己绞尽脑汁闭门造车,可以参考成熟项目的做法。Java Spring 是我参考的对象,一般的Spring 项目,有着很明确分层结构,虽然初期需要写较多的代码,但是给后期的代码维护,着实带来了很多便利。

一般会分为如下层级:

Controller
Service
Repository( DAO )
(Mapper,可选,如果使用了Mybatis的话)
Model
复制代码

结合Django的特性,我们发现Django的Manager层(即: XXModel.objects ),其实是对应着 DAO 层的,只不过大家的叫法不同。

我们不妨将抽取的单独层,叫做DAO 好了,后面我们也会看到,它其实就是对 Manager 层的API进行组合,对上提供一些通用的操作。

如何写

在正式写之前,我们可以先根据实际经验,思考:应该提供哪些通用的API?下面是我根据自己的经验,得出的结论:

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

那么通过什么手段实现呢?得益于 Python 强大的语言特性,让我们的代码可以不必写得像 Java 那样冗长乏味。我的步骤如下:

BaseDAO

下面是代码片段:

# 基于 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 层,让自己的代码越写越舒服。


以上所述就是小编给大家介绍的《为你的Django APP 写一层 DAO》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Django 1.0 Template Development

Django 1.0 Template Development

Scott Newman / Packt / 2008 / 24.99

Django is a high-level Python web application framework designed to support the rapid development of dynamic websites, web applications, and web services. Getting the most out of its template system a......一起来看看 《Django 1.0 Template Development》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具