Напишите слой DAO для вашего приложения Django.

задняя часть Python Spring Django

Без хорошей многослойности веб-проект в конечном итоге рухнет.

причина

Проекты Django, как правило, делятся на приложения, и каждое приложение имеет схожую структуру: каждый «занимается своими делами», что немного похоже на микросервис. Но код на Django пишут многие, определенных правил и норм нет, тысяча человек и тысяча стилей. Даже прямые голые вызовы появляются на уровне контроллера.UserModel.objects.filterне редкость. Однако мы обнаружили, что многие операции с базой данных являются общими, и в настоящее время необходимо извлечь один слой.

объект ссылки

Как организовать и проектировать наш слой? Нам не нужно стоять наши мозги за закрытыми дверями, мы можем обратиться к практике зрелых проектов. Java Spring - это объект моей ссылки. Проект общего пружины имеет очень четкую иерархическую структуру. Хотя на начальном этапе необходимо написать больше кода, он действительно приносит много удобства для более позднего обслуживания кода.

Обычно его делят на следующие уровни:

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

Объединив функции Django, мы обнаружим, что уровень менеджера Django (т.е.:XXModel.objects), что на самом деле соответствует слою DAO, но имя у всех разное.

С таким же успехом мы могли бы назвать извлеченный отдельный слой DAO.Позже мы увидим, что на самом деле это комбинация API уровня Manager и предоставляет некоторые общие операции над ним.

как писать

Прежде чем написать его официально, мы можем подумать об этом, основываясь на реальном опыте: какие общие API должны быть предоставлены? Вот мои выводы, основанные на собственном опыте:

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

Так с помощью чего? Благодаря мощным языковым возможностям Python наш код не нужно писать так утомительно и утомительно, как на Java. Мои шаги следующие:

  1. Сначала инкапсулируйте базовый родительский класс и поместите в него все общие операции, которые называютсяBaseDAOБар.
  2. Остальные наследуют этот родительский класс.
  3. Используется на уровне контроллера или службы

Вот фрагмент кода:

# 基于 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 используйте:

某个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 общего назначения».

Кроме того, популярная ORM в мире Python — это не только Django ORM. Например, SQLAlchemy и т. д., вы также можете инкапсулировать аналогичный уровень DAO, чтобы сделать собственный код более удобным для написания.