模型ORM

一、前言

看别人源码写的简易ORM

二、代码

"""
定义一个模型的基类和元类
"""
import asyncio
from datetime import datetime
from typing import Dict

from 工具.motorORM.校验异常 import FieldValidationError
from 工具.motorORM.校验类 import Field, StringField
from 数据库.操作 import motor_obj


class ModelMeta(type):
    """
    模型的元类
    在创建类的时候,将变量名传递到验证类的实例里,并进行校验
    """

    def __new__(mcs, name, bases, attrs):
        # 如果是model基类,就直接创建
        if name == "Model":
            return type.__new__(mcs, name, bases, attrs)
        # 判断abstract,决定是否过滤
        metainfo = attrs.get('Meta', None)
        if getattr(metainfo, "abstract", None):
            return type.__new__(mcs, name, bases, attrs)

        newobj = type.__new__(mcs, name, bases, attrs)
        # 剩下的都是标准子类,进行过滤
        items = {}
        fields = {}
        for k, v in attrs.items():
            if isinstance(v, Field):
                v.__set__("name", k)
                items[k] = 1
                fields[k] = v
        setattr(newobj, "fields", fields)
        # 设置默认表名
        if not getattr(metainfo, "tablename", None):
            setattr(metainfo, "tablename", [])
        # 设置默认排序
        if not getattr(metainfo, "ordering", None):
            setattr(metainfo, "ordering", [])
        setattr(newobj, "items", items)
        setattr(newobj, "_meta", metainfo)
        setattr(newobj, "motor_obj", motor_obj)
        return newobj


class Model(metaclass=ModelMeta):
    """
    模型的基类
    """
    motor_obj = motor_obj

    def __init__(self, **kwargs):
        # 初始化的时候,从fields取出类的字段,给实例
        for k, v in self.fields.items():
            self[k] = v
        # 初始化时,从meta获取数据库链接
        self.db = self.motor_obj.db[self._meta.tablename]
        # 初始化时,如果传递进来数值,将赋值到self.items上
        self.set_items_value(**kwargs)
        # 定义空字段进行群查过滤条件
        self.filteritem = {}
        # 定义排序字段
        self.order = []

    def __getitem__(self, item):
        return self.__dict__[item]

    def __setitem__(self, key, value):
        self.__dict__[key] = value

    def __set__(self, instance, value):
        self[instance].value = value

    def set_items_value(self, **kwargs):
        # 过滤出模型含有的字段,赋值给验证实例,触发验证。
        for k in self.items.keys():
            if kwargs.get(k, None):
                self[k].__set__("value", kwargs[k])

    # 将数据整理成字典,方便插入
    def get_dict(self):
        jieguo = {}
        for i in self.items.keys():
            jieguo.update(self[i].getmodelfield())
        return jieguo

    async def create(self, **kwargs):
        # 创建方法,先把值都传递进校验类实例里面进行校验
        self.set_items_value(**kwargs)
        # 检查唯一性
        await self.check_unique()
        # 如果没问题,就开始插入
        jieguo1 = await self.db.insert_one(**self.get_dict())
        return jieguo1

    async def check_unique(self):
        # 过滤出唯一字段,进行唯一性验证
        for i in self.items.keys():
            if self[i].unique:
                await self.get_one(self[i].getmodelfield())
        # 遍历联合唯一列表,进行唯一性验证
        for i in self._meta.unique_together:
            zidian = {}
            for t in i:
                zidian.update({t: self[t]})
            await self.get_one(zidian)

    # 排查唯一字段和联合唯一字段
    async def get_one(self, item=None):
        if item:
            jieguo = await self.db.find_one(item)
            if jieguo:
                keylist = item.keys()
                raise FieldValidationError({",".join(keylist): f"不是唯一的"})

    # 查询一个数据
    async def first(self, getitem=None):
        if not getitem:
            jieguo = await self.db.find_one(self.filteritem, self.items)
        else:
            jieguo = await self.db.find_one(self.filteritem, getitem)
        return jieguo

    # 查询多个数据
    async def all(self, fenye=None, getitem=None):
        jieguo = []
        # 先获取数据
        if getitem:
            shuju = self.db.find(self.filteritem, getitem)
        else:
            shuju = self.db.find(self.filteritem, self.items)
        # 然后排序
        if isinstance(self.order, str):
            shuju = shuju.sort(self.order)
        elif isinstance(self.order, list) or isinstance(self.order, tuple):
            shuju = shuju.sort(",".join(self.order))
        # 最后分页
        if fenye:
            shuju = shuju.skip((fenye["p"] - 1) * fenye["psize"]).limit(fenye["psize"])
        for i in await shuju.to_list(length=100):
            jieguo.append(i)
        return jieguo

    # 更新数据,不能修改editable为true的
    async def update(self, obj: Dict, **kwargs):
        """
        replace_one()是全部修改,不需要“$set”
        update_one()是部分修改,需要添加”$set“
        """
        # 过滤出要更新的字段,注意过滤不能修改的字段
        keylist = self.items.keys()
        updateitem = {}
        for k, v in kwargs.items():
            if k in keylist and not self[k].editdisable:
                updateitem[k] = v
        if updateitem != {}:
            # 过滤完数据后,放到验证类实例里面进行验证
            self.set_items_value(**updateitem)
            # 验证完后,再取出来
            shuju = {}
            for i in updateitem.keys():
                shuju.update(self[i].getmodelfield())

            # 更新的时候,需要修改changetime
            shuju["changeTime"] = datetime.now()
            jieguo = await self.db.update_one(obj, shuju)
            return jieguo
        return obj

    async def delete(self, itemid: str):
        # 使用逻辑删除方式删除数据
        obj = await self.db.find_one({"id": itemid})
        if not obj:
            raise FieldValidationError({itemid: f"id:{itemid}对应的数据不存在"})
        jieguo = await self.db.update_one(obj, {"$set": {"deleteTime": datetime.now(), "isDelete": True}})
        return jieguo

    async def filter(self, item):
        self.filteritem.update(item)

    def __str__(self):
        return self._meta.tablename

    # 测试用方法,一会删除
    async def getall(self):
        thisjieguo = []
        for i in await self.db.find().to_list(100):
            thisjieguo.append(i)
        return thisjieguo

    class Meta:
        abstract = False  # 基类
        unique_together = ()  # 联合唯一
        ordering = ""  # 默认排序
        tablename = ""  # 表名,必填


class Ceshi(Model):
    """
    测试类
    """
    pk = StringField(max_length=50, help_text="主键ID")

    class Meta:
        tablename = "worker"


if __name__ == '__main__':
    ceshi = Ceshi(id=6565)
    # loop = asyncio.get_event_loop()
    # jieguo = loop.run_until_complete(ceshi.getall())
    print("打印结果")
    print(ceshi.id, type(ceshi.id))

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值