Python元类编程实现简易版djangoORM

Python 元类编程实现 DjangoORM

需求说明

使用 Python 元类编程实现简单的 Django Model 定义、校验以及构造生成 sql 语句。

我们实例化一个User对象的时候可以有三种方式

  1. 可以user=User()、user(name = None, age = 18)
  2. 可以user=User()、user['name] =None,user[‘age’] = 18
  3. 还可以 user=User()、user.name = None, user.age = 18

前面两种都可以通过继承字典dict 实现,第三种,用getattr和setattr

一、定义 Field 类

我们只定义了两个 Field 类:IntField、CharField,用于定义 Model 支持的数据类型以及对值的类型、长度、大小进行一些简单校验。

import numbers

class Field(object):
    def __init__(self):
        self._value = None
    def __str__(self):
        return self._value
    def __get__(self, instance, owner):
        return self._value

class IntField(Field):
    def __init__(self, db_column=None, min_value=None, max_value=None, *args, **kwargs):
        self.db_column = db_column
        if min_value is not None and not isinstance(min_value, numbers.Integral):
            raise ValueError('min_value must be int')
        self.min_value = min_value
        if max_value is not None and not isinstance(max_value, numbers.Integral):
            raise ValueError('max_value must be int')
        self.max_value = max_value
        if min_value is not None and max_value is not None:
            if min_value > max_value:
                raise ValueError('min_value must smaller than max_value')
        super().__init__()

    def __set__(self, instance, value):
        if not isinstance(value, numbers.Integral):
            raise ValueError('IntField value must be int')
        if self.min_value is not None and value < self.min_value:
            raise ValueError(f'IntField value must bigger than {self.min_value}')
        if self.max_value is not None and value > self.max_value:
            raise ValueError(f'IntField value must smaller than {self.max_value}')
        self._value = value

class CharField(Field):
    def __init__(self, db_column=None, max_length=None, *args, **kwargs):
        self.db_column = db_column
        if max_length is not None and not isinstance(max_length, numbers.Integral):
            raise ValueError('max_length must be int')
        elif max_length < 0:
            raise ValueError('max_length must bigger than 0')
        self.max_length = max_length
        super().__init__()

    def __set__(self, instance, value):
        if not isinstance(value, str):
            raise ValueError('CharField value must be str')
        if self.max_length is not None and len(value) > self.max_length:
            raise ValueError(f'CharField value length must smaller than {self.max_length}')
        self._value = value

第一步没什么好说的,提醒一点:数据验证很重要。

二、定义元类

该元类主要用于将 Model 中定义的 column 以及 db_table 等初始化到 Model 的实例对象上。

class ModelMetaClass(type):
# name -> class.name
# bases -> 继承的基类名
# attrs 可变参数
    def __new__(cls, name, bases, attrs, **kwargs):
        fields = {}
        for key, value in attrs.items():
            if isinstance(value, Field):
                fields[key] = value
        _meta = {}
        attr_meta_class = attrs.get('Meta', None)
        db_table = name.lower()
        if attr_meta_class is not None:
            db_table = getattr(attr_meta_class, 'db_table', db_table)
        _meta['db_table'] = db_table
        attrs['fields'] = fields
        attrs['_meta'] = _meta
        # 调用父类的__new__方法。
				return super().__new__(cls, name, bases, attrs, **kwargs)
				

第二步中值得注意的:attr_meta_class 中就是将来,django orm中class Meta:里的字段。

这里,如果attr_meta_class 字段为空,将来表的字段就是name.lower() → 类的名称

如果不为空就在attr_meta_class 取db_table 字段的值作为将来表的名称

三、定义 Model 基类

Model 基类主要用于支持 Model 创建对象的时候及初始化 column 值以及定义通用的 orm 处理逻辑函数。

class BaseModel(metaclass=ModelMetaClass):
    def __init__(self, *args, **kwargs):
        for key, value in kwargs.items():
						# 属性访问
						
            setattr(self, key, value)
        super().__init__()

    def save(self):
        db_table = self._meta.get('db_table')
        fields = []
        values = []
				# 这个self.fields 是继承元类的fields字段,里面保存的就是未来的 字段名和值
				# 也可以换成 in self.attrs['fields'].items()  -> 不可以换成这个。
				# 原因是因为 BaseModel初始化中没有继承父类的attrs属性

        for key, field in self.fields.items():
						# 这里也可以用继承字典的方式,设置 属性分配。
            db_column = field.db_column
            if db_column is None:
                db_column = key.lower()
            fields.append(db_column)
            value = getattr(self, key)
            values.append(str(value))
        sql = f"insert {db_table}({','.join(fields)}) value({','.join(values)})"
        print(sql)

四、定义 Model 实体类

class User(BaseModel):
    name = CharField(max_length=10)
    age = IntField(min_value=0, max_value=100)
    class Meta:
        db_table = 'user'
    def __str__(self):
        return self.name

五、测试

user = User(name='hmb', age=19)
# hmb = User()
# hmb['name'] = 'hmb' 这种分配方式不可行,在第三步中没有实现。
# hmb['age'] = 18
hmb2 = User()
hmb2.name = 'hmb'
hmb2.age = 18
user.save()
# hmb.save()
hmb2.save()

创建 model 类,以及调用 save 会输出经过 orm 转换后的 sql 语句:insert user(name,age) value(python,18) >

六、输出结果

insert user(name,age) value(hmb,18)
insert user(name,age) value(hmb,18)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值