# 需求: 定義一個class,
# orm系統:將類映射到數據庫的一張表,對類操作,就可以將數據寫入表中,這樣就可以脫離SQL語句。
# 爲了實現屬性賦值報錯的功能,就需要用到前面提到的屬性描述符。
import numbers
class Field:
pass
class IntField:
# 數據描述符
# 此處的類如何變成屬性描述符?只需要實現get方法,或者是set方法,類裏面的任一個方法 都是屬性描述符,可以用於後續規則的檢查
# IntField實現對Int類型的檢查
def __init__(self,db_column,min_value=None,max_value=None): # 初始化,涉及到min_vlaue與max_value兩個參數,可以默認不設置,均爲None
self._value = None # 此處爲什麼用self._value? 屬性描述符保存值的時候,是保存在value中的。
# 用單下劃線_value,指明這是一個內部的變量,不希望通過self.value來訪問,這是一種規範,非必須。
self.min_value = min_value
self.max_value = max_value
self.db_column = db_column
if min_value is not None:
if isinstance(min_value,numbers.Integral): # 判斷,如果有數據傳入,是否傳入的爲整型?
raise ValueError("min_value must be int")
elif min_value < 0: # 用於判斷,當輸入必須爲正整數的情形
raise ValueError("min_value must be a postive int")
if max_value is not None:
if isinstance(max_value,numbers.Integral): # 判斷,如果有數據傳入,是否傳入的爲整型?
raise ValueError("max_value must be int")
elif max_value < 0: # 用於判斷,當輸入必須爲正整數的情形
raise ValueError("max_value must be a postive int")
if min_value is not None and max_value is not None:
if min_value > max_value:
raise ValueError("min_value must be smaller than max_value")
def __get__(self,instance,owner): #
return self._value # __set__中定義了value參數,此處返回,需要確保前後一致,因此爲self._value
def __set__(self,instance,value): # 賦值/set的時候,進行了參數類型的檢查
if not isinstance(value,numbers.Integral):
raise ValueError("int value required")
if value < self.min_value or value > self.max_value:
raise ValueError("value must betwen min_value and max_value")
self._value = value # 將傳進來的value賦給IntFiled這個class中
class CharField:
def __init__(self,db_column,max_length=None):
self._value = None
self.db_column = db_column
self.max_length = max_length
if max_length is None:
raise ValueError("You must specify max_lenth for charfield")
def __get__(self,instance,owner):
return self._value
def __set__(self,instance,value):
if not isinstance(value,str):
raise ValueError("string value required")
if len(value) > self.max_length:
raise ValueError("value length execess length of max_length")
self._value = value # 將傳進來的value賦給IntFiled這個class中
class ModelMetaClass(type):
def __new__(cls,name,bases,attrs,**kwargs): # 此處,*args: tuple自動會拆包,分別賦值給name,基類,屬性中。
if name == "BaseModel":
return super().__new__(cls,name,bases,attrs,**kwargs)
fields = {}
for key,value in attrs.items():
if isinstance(value,Field):
fields[key] = value
attrs_meta = attrs.get("Meta",None)
_meta = {}
db_table = name.lower()
if attrs_meta is not None:
table = getattr(attrs_meta,"db_table",None)
if table is not None:
db_table = table
_meta["db_table"] = db_table
attrs["_meta"] = _meta
attrs["fields"] = fields
del attrs["meta"]
return super().__new__(cls,name,bases,attrs,**kwargs) # __new__方法一定要return一個類
class BaseModel(metaclas = ModelMetaClass):
def __init__(self,*args,**kwargs):
for key,value in kwargs.items():
setattr(self,key,value)
return super().__init__()
def save(self):
fields = []
values = []
for key,value in self.fields.items():
db_column = value.db_column
if db_column == None:
db_column = key.lower()
fields.append(db_column)
value = getattr(self,key)
values.append(str(value))
sql = "insert {db_table}({fields})user(name,age) value({values})".format(db_table = self._meta["db_table"],fields = ",".join(fields),values=",".joint(values))
class User(BaseModel): # 希望在創建User時,可以添加很多自己的屬性註冊到User的屬性中來,將元類寫的複雜一定,後續的類創建就會省很多代碼。
name = CharField(db_column="",max_length = 10) # db_column爲數據庫中所對應的列,max_length定義數據庫中最大長度
age = IntField(db_column="",min_value=1,max_value=100)# db_column爲數據庫中所對應的字段,max_length定義數據庫中最大長度
class Meta: # 定義表的列
db_table = "user" # 定義數據表的名稱,如果此處不賦值,則默認會用外面的Class,即User的小寫user進行賦值。
if __name__=="__main__":
user = User()
user.name="Tom"
user.age=18
user.save()
1.ORM是什麼
ORM是python編程語言後端web框架Django的核心思想,“Object Relational Mapping”,即對象-關係映射,簡稱ORM。
一句話理解就是:創建一個實例對象,用創建它的類名當作數據庫表名,用創建他的類屬性對應數據表的字段,當對這個實例對象操作時,能夠對應MySQL語句。
demo:
class User(父類省略):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
...省略...
u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
u.save()
# 對應如下sql語句
# insert into User (username,email,password,uid)
# values ('Michael','[email protected]','my-pwd',12345)
說明
- 所謂的ORM就是讓開發者在操作數據庫的時候,能夠像操作對象時通過
xxxx.屬性=yyyy
一樣簡單,這是開發ORM的初衷 - 只不過ORM的實現較爲複雜,Django中已經實現了 很複雜的操作,本節知識 主要通過完成一個 insert相類似的ORM,理解其中的道理就就可以了
2. 通過元類簡單實現ORM中的insert功能
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判斷是否需要保存
for k, v in attrs.items():
# 判斷是否是元組類型
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
# 刪除這些已經在字典中存儲的屬性
for k in mappings.keys():
attrs.pop(k)
# 將之前的uid/name/email/password以及對應的對象引用、類名字
attrs['__mappings__'] = mappings # 保存屬性和列的映射關係
attrs['__table__'] = name # 假設表名和類名一致
return type.__new__(cls, name, bases, attrs)
class User(metaclass=ModelMetaclass):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
# 當指定元類之後,以上的類屬性將不在類中,而是在__mappings__屬性指定的字典中存儲
# 以上User類中有
# __mappings__ = {
# "uid": ('uid', "int unsigned")
# "name": ('username', "varchar(30)")
# "email": ('email', "varchar(30)")
# "password": ('password', "varchar(30)")
# }
# __table__ = "User"
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None))
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join([str(i) for i in args]))
print('SQL: %s' % sql)
u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
# print(u.__dict__)
u.save()
執行的效果:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
SQL: insert into User (uid,username,email,password) values (12345,Michael,[email protected],my-pwd)
3. 完善對數據類型的檢測
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判斷是否需要保存
for k, v in attrs.items():
# 判斷是否是元組類型
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
# 刪除這些已經在字典中存儲的屬性
for k in mappings.keys():
attrs.pop(k)
# 將之前的uid/name/email/password以及對應的對象引用、類名字
attrs['__mappings__'] = mappings # 保存屬性和列的映射關係
attrs['__table__'] = name # 假設表名和類名一致
return type.__new__(cls, name, bases, attrs)
class User(metaclass=ModelMetaclass):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
# 當指定元類之後,以上的類屬性將不在類中,而是在__mappings__屬性指定的字典中存儲
# 以上User類中有
# __mappings__ = {
# "uid": ('uid', "int unsigned")
# "name": ('username', "varchar(30)")
# "email": ('email', "varchar(30)")
# "password": ('password', "varchar(30)")
# }
# __table__ = "User"
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None))
args_temp = list()
for temp in args:
if isinstance(temp, int):
# 判斷如果是數字類型
args_temp.append(str(temp))
elif isinstance(temp, str):
# 判斷如果是字符串類型
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql)
u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
# print(u.__dict__)
u.save()
運行效果如下:
Found mapping: uid ==> ('uid', 'int unsigned')
Found mapping: name ==> ('username', 'varchar(30)')
Found mapping: email ==> ('email', 'varchar(30)')
Found mapping: password ==> ('password', 'varchar(30)')
SQL: insert into User (uid,username,email,password) values (12345,'Michael','[email protected]','my-pwd')
4. 抽取到基類中
class ModelMetaclass(type):
def __new__(cls, name, bases, attrs):
mappings = dict()
# 判斷是否需要保存
for k, v in attrs.items():
# 判斷是否是元組類型
if isinstance(v, tuple):
print('Found mapping: %s ==> %s' % (k, v))
mappings[k] = v
# 刪除這些已經在字典中存儲的屬性
for k in mappings.keys():
attrs.pop(k)
# 將之前的uid/name/email/password以及對應的對象引用、類名字
attrs['__mappings__'] = mappings # 保存屬性和列的映射關係
attrs['__table__'] = name # 假設表名和類名一致
return type.__new__(cls, name, bases, attrs)
class Model(object, metaclass=ModelMetaclass):
def __init__(self, **kwargs):
for name, value in kwargs.items():
setattr(self, name, value)
def save(self):
fields = []
args = []
for k, v in self.__mappings__.items():
fields.append(v[0])
args.append(getattr(self, k, None))
args_temp = list()
for temp in args:
# 判斷入如果是數字類型
if isinstance(temp, int):
args_temp.append(str(temp))
elif isinstance(temp, str):
args_temp.append("""'%s'""" % temp)
sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(args_temp))
print('SQL: %s' % sql)
class User(Model):
uid = ('uid', "int unsigned")
name = ('username', "varchar(30)")
email = ('email', "varchar(30)")
password = ('password', "varchar(30)")
u = User(uid=12345, name='Michael', email='[email protected]', password='my-pwd')
# print(u.__dict__)
u.save()
#!/user/bin/env python
# -*- coding:utf-8 -*-
# 需求
import numbers
class Field:
pass
class IntField(Field):
def __init__(self, db_column, min_value=None, max_value=None):
self._value = None
self.db_column = db_column
self.min_value = min_value
self.max_value = max_value
if min_value is not None:
if not isinstance(min_value, numbers.Integral):
raise ValueError('min_value must be int')
elif min_value < 0:
raise ValueError('min_value must be positive int')
if max_value is not None:
if not isinstance(max_value, numbers.Integral):
raise ValueError('max_value must be int')
elif min_value < 0:
raise ValueError('max_value must be positive int')
if min_value is not None and max_value is not None:
if min_value > max_value:
raise ValueError('min_value must be smaller than max_value')
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, numbers.Integral):
raise ValueError('int value need')
if value < self.min_value or value > self.max_value:
raise ValueError('value must between min_value and max_value')
self._value = value
class CharField(Field):
def __init__(self, db_column, max_length=None):
self._value = None
self.db_column = db_column
if max_length is None:
raise ValueError('you must spcify max_length for CharField')
self.max_length = max_length
def __get__(self, instance, owner):
return self._value
def __set__(self, instance, value):
if not isinstance(value, str):
raise ValueError('string value need')
if len(value) > self.max_length:
raise ValueError('value len excess len of max_length')
self._value = value
class ModelMetaClass(type):
def __new__(cls, name, bases, attrs, **kwargs):
if name == 'BaseModel':
return super().__new__(cls, name, bases, attrs, **kwargs)
fields = {}
for key, value in attrs.items():
if isinstance(value, Field):
fields[key] = value
attrs_meta = attrs.get('Meta', None)
_meta = {}
db_table = name.lower()
if attrs_meta is not None:
table = getattr(attrs_meta, 'db_table', None)
if table is None:
db_table = table
_meta['db_table'] = db_table
attrs['_meta'] = _meta
attrs['fields'] = fields
del attrs['Meta']
return super().__new__(cls, name, bases, attrs, **kwargs)
class BaseModel(metaclass=ModelMetaClass):
def __init__(self, *args, **kwargs):
for key, value in kwargs.items():
setattr(self, key, value)
return super().__init__()
def save(self):
fields = []
values = []
for key, value in self.fields.items():
db_column = value.db_column
if db_column is None:
db_column = key.lower()
fields.append(db_column)
value = getattr(self, key)
values.append(str(value))
sql = 'insert {db_table}({fields}) value({values})'.format(db_table=self._meta['db_table'], fields=','.join(fields), values=','.join(values))
print(sql)
class User(BaseModel):
name = CharField(db_column='name', max_length=10)
age = IntField(db_column='age', min_value=0, max_value=100)
class Meta:
db_table = 'user'
if __name__ == '__main__':
user = User()
user.name = 'zy'
user.age = 21
user.save()
insert user(name,age) value(zy,21)