溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

60數(shù)據(jù)庫5_元編程_ORM框架

發(fā)布時間:2020-06-17 04:57:58 來源:網(wǎng)絡(luò) 閱讀:282 作者:chaijowin 欄目:編程語言

?

?

目錄

meta ?programming,元編程:... 1

ORM... 5

字段類的實現(xiàn):... 5

session類的實現(xiàn):... 8

自定義Model類的實現(xiàn):... 9

使用元類改造Model... 10

引擎類:... 12

總結(jié):... 13

?

?

?

meta programming,元編程:

概念來自LISPsmaltalk;

我們寫程序是直接寫代碼,是否能用代碼來生成未來我們需要的代碼?這就是元編程;

用來生成代碼的程序稱為元程序meta program,編寫這種程序就稱為元編程meta programming;

py能通過反射實現(xiàn)元編程(元語言);

在框架中用的多;

?

?

type類:

typeobject糾纏在一起,關(guān)系復(fù)雜;

所有自定義的類都是type的子類,即type類的實例;

?

class type(object):

??? def __init__(cls, what, bases=None, dict=None): # known special case of type.__init__?? #what類名字,bases繼承列表,dict類對象(類屬性字典),這些與class定義的類相關(guān)

??????? """

??????? type(object_or_name, bases, dict)?? #借助type構(gòu)造任何類,用代碼來生成代碼即元編程

??????? type(object) -> the object's type?? #返回對象的類型,如type(10)

??????? type(name, bases, dict) -> a new type?? #返回一個新的類型

??????? # (copied from class doc)

??????? """

??????? pass

?

總結(jié):

元類是制造類的工廠,是生成類的類;

定義一個元類,需要使用type(name,bases,dict),也可以繼承type;

構(gòu)造好元類,就可以在類定義時使用關(guān)鍵字參數(shù)metaclass指定元類,可使用最原始的metatype(name, bases, dict)方式構(gòu)造一個元類;

元類的__new__()方法中,可獲取元類信息、當前類、基類、類變量信息;

元編程一般用于框架開發(fā)中;

開發(fā)中,除非明確知道自己在干什么,否則不要隨便使用元編程99%的情況下用不到元類,可能有些程序員一輩子都不會使用元類;

DjangoSQLAlchemy使用了元類,這樣我們使用起來很方便;

?

?

例:

XClass = type('myclass', (object,), {'a': 100, 'b': 'string'})

print(XClass)

print(type(XClass))

print(XClass.__dict__)

print(XClass.mro())

輸出:

<class '__main__.myclass'>

<class 'type'>

{'b': 'string', '__dict__': <attribute '__dict__' of 'myclass' objects>, '__weakref__': <attribute '__weakref__' of 'myclass' objects>, 'a': 100, '__module__': '__main__', '__doc__': None}

[<class '__main__.myclass'>, <class 'object'>]

?

1

def __init__(self):

??? self.x = 100

?

def show(self):

??? print(self.__dict__)

?

XClass = type('myclass', (object,), {'a': 100, 'b': 'string', '__init__': __init__, 'show': show})

print(XClass)

print(type(XClass))

print(XClass.__dict__)

print(XClass.mro())

輸出:

<class '__main__.myclass'>

<class 'type'>

{'a': 100, '__weakref__': <attribute '__weakref__' of 'myclass' objects>, '__dict__': <attribute '__dict__' of 'myclass' objects>, 'b': 'string', 'show': <function show at 0x0000000000801488>, '__module__': '__main__', '__doc__': None, '__init__': <function __init__ at 0x00000000008012F0>}

[<class '__main__.myclass'>, <class 'object'>]

?

2

同例1;

class MyClass:

??? def __init__(self):

??????? print('init')

?

??? def show(self):

??????? print('show')

?

print(MyClass)

print(type(MyClass))

print(MyClass.__dict__)

print(MyClass.mro())

輸出:

<class '__main__.MyClass'>

<class 'type'>

{'show': <function MyClass.show at 0x00000000011B1598>, '__module__': '__main__', '__doc__': None, '__dict__': <attribute '__dict__' of 'MyClass' objects>, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__init__': <function MyClass.__init__ at 0x00000000011B1510>}

[<class '__main__.MyClass'>, <class 'object'>]

?

例:

class XMeta(type):

??? # pass

??? def __new__(cls, *args, **kwargs):?? #__new__()可用作攔截;cls,不是普通的類,而是與type類等同,稱為元類;參數(shù)(cls,*args,**kwargs)等同于(cls,what,bases,dict)

??????? print(1, cls)?? #打印元類

??????? print(2, args)?? #what,打印三元組(what,bases,dict)

??????? print(3, kwargs)?? #bases,{}

??????? print(4, super())?? #dict

??????? print(5, type(cls))

??????? return super().__new__(cls, *args, **kwargs)

??????? # return 1

??????? # return super().__new__(cls, what, bases, dict)

?

??? # def __new__(cls, name, bases, dict):

??? #???? print(1, cls)

??? #???? print(2, name)

??? #???? print(3, bases)

??? #???? print(4, dict)

??? #???? return super().__new__(cls, name, bases, dict)

?

print(XMeta)

print(type(XMeta))

print(XMeta.__dict__)

# print(XMeta.mro())

?

class A(metaclass=XMeta):?? #方式一,通過metaclass關(guān)鍵字指定元類,此處不是繼承而是替換元類

??? # pass

??? id = 100

??? def __init__(self):

??????? print('###A init###')

?

A()

print(A)

print(type(A))

?

class B(A):?? #方式二,通過繼承方式得到元類

??? # pass

??? def __init__(self):

??????? super().__init__()

??????? print('###B init###')

?

B()

?

C = XMeta('tom', (), {})?? #方式三,用元類構(gòu)建其它類

?

print(type(A), type(B))

輸出:

<class '__main__.XMeta'>

<class 'type'>

{'__doc__': None, '__module__': '__main__', '__new__': <staticmethod object at 0x000000000076E278>}

1 <class '__main__.XMeta'>

2 ('A', (), {'__init__': <function A.__init__ at 0x0000000000761598>, '__module__': '__main__', '__qualname__': 'A', 'id': 100})

3 {}

4 <super: <class 'XMeta'>, <XMeta object>>

5 <class 'type'>

###A init###

<class '__main__.A'>

<class '__main__.XMeta'>

1 <class '__main__.XMeta'>

2 ('B', (<class '__main__.A'>,), {'__init__': <function B.__init__ at 0x0000000000761730>, '__module__': '__main__', '__qualname__': 'B'})

3 {}

4 <super: <class 'XMeta'>, <XMeta object>>

5 <class 'type'>

###A init###

###B init###

1 <class '__main__.XMeta'>

2 ('tom', (), {})

3 {}

4 <super: <class 'XMeta'>, <XMeta object>>

5 <class 'type'>

<class '__main__.XMeta'> <class '__main__.XMeta'>

?

?

?

ORM

object relation map,對象關(guān)系映射,對象和關(guān)系之間的映射,使用面向?qū)ο蟮姆绞絹聿僮?/span>DB;

關(guān)系模型和py對象之間的映射;

?

table-->class?? #表映射為類

row-->object?? #行映射為實例

column-->property?? #字段映射為類屬性

?

例:

實現(xiàn)ORM框架;

表由字段構(gòu)成,表對應(yīng)類,類屬性對應(yīng)字段;

?

?

字段類的實現(xiàn):

字段特征有:name,column,type,pk,uk(unique key),index,nullable,default,auto_increment,所以字段可用類來描述;

字段類要提供對數(shù)據(jù)的校驗功能,如聲明字段是int類型,應(yīng)要判斷數(shù)據(jù)是不是整型;

字段有多種類型,不同類型有差異,使用繼承的方式實現(xiàn);

字段現(xiàn)定義為類屬性,而這個類屬性適合使用類來描述,即描述器;

?

例:

class Field:

??? def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None):

??????? self.name = name

??????? if column is None:

??????????? self.column = name

??????? else:

??????????? self.column = column

??????? self.pk = pk

??????? self.unique = unique

??????? self.index = index

??????? self.nullable = nullable

??????? self.default = default

?

??? def validate(self, value):

??????? raise NotImplementedError

?

??? def __get__(self, instance, owner):

??????? if instance is None:

??????????? return self

??????? return instance.__dict__[self.name]

?

??? def __set__(self, instance, value):

??????? self.validate(value)

??????? instance.__dict__[self.name] = value

?

??? def __str__(self):

??????? return '{} <{}>'.format(self.__class__.__name__, self.name)

?

??? __repr__ = __str__

?

class IntField(Field):

??? def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None, auto_increment=False):

??????? super().__init__(name, column, pk, unique, index, nullable, default)

??????? self.auto_increment = auto_increment

?

??? def validate(self, value):

??????? # pass

??????? if value is None:

??????????? if self.pk:

??????????????? raise TypeError('{} is pk, not None'.format(self.name))

??????????? if not self.nullable:

??????? ????????raise TypeError('{} required'.format(self.name))

??????? else:

??????????? if not isinstance(value, int):

??????????????? raise TypeError('{} should be integer'.format(self.name))

?

class StringField(Field):

??? def __init__(self, name, column=None, pk=False, unique=False, index=False, nullable=True, default=None, length=False):

??????? super().__init__(name, column, pk, unique, index, nullable, default)

??????? self.length = length

?

??? def validate(self, value):

??????? # pass

??????? if value is None:

??????????? if self.pk:

??????????????? raise TypeError('{} is pk, not None'.format(self.name))

??????????? if not self.nullable:

??????????????? raise TypeError('{} required'.format(self.name))

??????? else:

??????????? if not isinstance(value, str):

??????????????? raise TypeError('{} should be string'.format(self.name))

??????????? if len(value) > self.length:

??????????????? raise ValueError('{} is too long'.format(value))

?

例:

Student類的操作對應(yīng)表的CRUD操作,若使用pymysql,應(yīng)用cursor對象的execute方法;

增加、修改數(shù)據(jù)自定義為save()方法,數(shù)據(jù)庫連接從外部傳入,這應(yīng)是一個全局變量;

class Student:

??? id = IntField('id', 'id', True, nullable=False, auto_increment=True)

??? name = StringField('name', nullable=False, length=64)

??? age = IntField('age')

?

??? def __init__(self, id, name, age):

??????? self.id = id

??????? self.name = name

??????? self.age = age

?

??? def __str__(self):

??????? return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

?

??? __repr__ = __str__

?

??? def save(self, conn:pymysql.connections.Connection):

??????? sql = 'insert into student (id, name, age) values (%s, %s, %s)'

??????? with conn as cursor:

??????????? cursor.execute(sql, (self.id, self.name, self.age))

?

?

session類的實現(xiàn):

每一次數(shù)據(jù)庫操作都是在一個會話中完成,將cursor的操作封裝到會話中;

?

例:

class Session:

??? def __init__(self, conn: pymysql.connections.connection):

??????? self.conn = conn

??????? self.cursor = None

?

??? def execute(self, query, *args):

??????? if self.cursor is None:

??????????? self.cursor = self.conn.cursor()

??????? self.cursor.execute(query, args)

?

??? def __enter__(self):

??????? self.cursor = self.conn.cursor()

??????? return self

?

??? def __exit__(self, exc_type, exc_val, exc_tb):

??????? self.cursor.close()

??????? if exc_type:

??????????? self.conn.rollback()

??????? else:

?????? ?????self.conn.commit()

?

class Student:

??? id = IntField('id', 'id', True, nullable=False, auto_increment=True)

??? name = StringField('name', nullable=False, length=64)

??? age = IntField('age')

?

??? def __init__(self, id, name, age):

??????? self.id = id

??????? self.name = name

??????? self.age = age

?

??? def __str__(self):

??????? return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

?

??? __repr__ = __str__

?

??? def save(self, session: Session):

??????? sql = 'insert into student (id, name, age) values (%s, %s, %s)'

??????? session.execute(sql, self.id, self.name, self.age)

?

?

自定義Model類的實現(xiàn):

Student這樣的類,若多建幾個,可發(fā)現(xiàn)千篇一律,每一個這樣的類,得定義一個名稱,對應(yīng)不同的表,都要先定義好類屬性,再__init__()初始化值,而這些值正好是定義好的類屬性;

CRUD操作也一樣;

?

設(shè)計一個Model類,增加一個__table__類屬性用來保存不同的表名;

?

例:

class Model:

??? def save(self, session: Session):

??????? names = []

??????? values = []

?

??????? for k, v in self.__class__.__dict__.items():

??????????? if isinstance(v, Field):

??????????????? if k in self.__dict__.keys():

??????????????????? names.append(k)

??????????????????? values.append(v)

?

??????? query = 'insert into {} ({}) values({})'.format(

??????????? self.__table__,

??????????? ','.join(names),

??????????? ','.join(['%s']*len(values)))

??????? print(query)

???? ???print(values)

?

class Student(Model):

??? __table__ = 'student'

??? id = IntField('id', 'id', True, nullable=False, auto_increment=True)

??? name = StringField('name', nullable=False, length=64)

??? age = IntField('age')

?

??? def __init__(self, id, name, age):

??????? self.id = id

??????? self.name = name

??????? self.age = age

?

??? def __str__(self):

??????? return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

?

??? __repr__ = __str__

?

s = Student(1, 'tom', 20)

s.save(None)

輸出:

insert into student (name,age,id) values(%s,%s,%s)

[StringField <name>, IntField <age>, IntField <id>]

?

?

使用元類改造Model

通過元類編程,增加了mapping類變量,里面存儲著已經(jīng)過濾出來的,字段類變量名=>Field對象映射;

例:

class ModelMeta(type):

??? def __new__(cls, name, bases, attrs: dict):?? #name類名,attrs類屬性字典

??????? if '__table__' not in attrs.keys():

??????????? attrs['__table__'] = name?? #默認添加表名為類名

?

??????? mapping = {}?? #方便以后查詢屬性名和字段實例

??????? primarykey = []

??????? for k, v in attrs.items():?? #k,類變量名稱字符串;v,對象

??????????? if isinstance(v, Field):

????? ??????????print(k, v)

??????????????? v.name = k

??????????????? if v.column is None:

??????????????????? v.column = k?? #沒有給字段名

?

??????????????? mapping[k] = v

?

??????????????? if v.pk:

??????????????????? primarykey.append(v)

?

??????? attrs['__mapping__'] = mapping?? #增加屬性

??????? attrs['__primary__'] = primarykey

?

??????? return super().__new__(cls, name, bases, attrs)

?

class Model(metaclass=ModelMeta):

??? def save(self, session: Session):

??????? names = []

??????? values = []

?

??????? for k, v in self.__class__.__dict__.items():

??????????? if isinstance(v, Field):

??????????????? if k in self.__dict__.keys():

??????????????????? names.append(k)

??????????????????? values.append(v)

?

??????? query = 'insert into {} ({}) values({})'.format(

??????????? self.__table__,

??????????? ','.join(names),

??????????? ','.join(['%s']*len(values)))

??????? print(query)

??????? print(values)

??????? # session.execute(query, *values)

?

class Student(Model):

??? __table__ = 'student'?? #有了元類,此句可省

??? id = IntField('id', 'id', True, nullable=False, auto_increment=True)

??? name = StringField('name', nullable=False, length=64)

??? age = IntField('age')

?

??? def __init__(self, id, name, age):

??????? self.id = id

??????? self.name = name

??????? self.age = age

?

??? def __str__(self):

??????? return 'Student({}, {}, {})'.format(self.id, self.name, self.age)

?

??? __repr__ = __str__

?

s = Student(1, 'tom', 20)

s.save(None)

print(Student.__dict__)

print(Model.__dict__)

輸出:

insert into student (id,age,name) values(%s,%s,%s)

[IntField <id>, IntField <age>, StringField <name>]

{'__doc__': None, '__primary__': [IntField <id>], 'id': IntField <id>, '__init__': <function Student.__init__ at 0x0000000002DC7158>, '__table__': 'student', '__repr__': <function Student.__str__ at 0x0000000002DC71E0>, '__module__': '__main__', 'age': IntField <age>, '__str__': <function Student.__str__ at 0x0000000002DC71E0>, 'name': StringField <name>, '__mapping__': {'id': IntField <id>, 'age': IntField <age>, 'name': StringField <name>}}

{'__doc__': None, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Model' objects>, '__weakref__': <attribute '__weakref__' of 'Model' objects>, '__primary__': [], '__table__': 'Model', 'save': <function Model.save at 0x0000000002DC70D0>, '__mapping__': {}}

?

?

?

引擎類:

實體類沒有提供數(shù)據(jù)庫連接,它也不應(yīng)該提供,實例類就應(yīng)只完成表和類的映射;

提供一個數(shù)據(jù)庫的包裝類:

1、負責數(shù)據(jù)庫連接;

2、負責CRUD操作,取代實體類的CRUD方法;

?

例:

class Engine:

??? def __init__(self, *args, **kwargs):

??????? self.conn = pymysql.connect(*args, **kwargs)

?

??? def save(self, instance: Student):

??????? names = []

??????? values = []

??????? for k, v in instance.__mapping__.items():

??????????? names.append('`{}`'.format(k))

??????????? values.append(instance.__dict__[k])

?

??????? query = "insert into {} ({}) values ({})".format(

??????????? instance.__table__,

??????????? ','.join(names),

??????????? ','.join(['%s']*len(values))

??????????? )

??????? print(query)

??????? print(values)

?

??????? # with self.conn as cursor:

??????? #???? with cursor:

??????? #? ???????cursor.execute(query, values)

?

s = Student(1, 'tom', 20)

# s.save(None)

print(Student.__dict__)

print(Model.__dict__)

print(s.__dict__)

?

engine = Engine('10.113.129.2', 'root', 'rootqazwsx', 'test1')

engine.save(s)

輸出:

id IntField <id>

age IntField <age>

name StringField <name>

{'__primary__': [IntField <id>], '__doc__': None, '__repr__': <function Student.__str__ at 0x0000000002DC81E0>, 'name': StringField <name>, '__table__': 'student', '__init__': <function Student.__init__ at 0x0000000002DC8158>, '__module__': '__main__', '__str__': <function Student.__str__ at 0x0000000002DC81E0>, 'id': IntField <id>, 'age': IntField <age>, '__mapping__': {'id': IntField <id>, 'age': IntField <age>, 'name': StringField <name>}}

{'save': <function Model.save at 0x0000000002DC80D0>, '__dict__': <attribute '__dict__' of 'Model' objects>, '__module__': '__main__', '__doc__': None, '__mapping__': {}, '__primary__': [], '__weakref__': <attribute '__weakref__' of 'Model' objects>, '__table__': 'Model'}

{'id': 1, 'name': 'tom', 'age': 20}

insert into student (`id`,`age`,`name`) values (%s,%s,%s)

[1, 20, 'tom']

?

?

總結(jié):

這是一個ORM框架的雛形,從這個例子就可以明白ORM框架的內(nèi)部原理;

學(xué)習(xí)一個ORM框架:

1、看Model類如何描述表、屬性和字段如何映射;

2、增刪改查方法調(diào)用如何轉(zhuǎn)化為SQL語句并執(zhí)行;

?

?

?

?

?


向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI