Django中的对象查询
Django框架自带了ORM,实现了一些比较强大而且方便的查询功能,这些功能和表无关。比如下面这个例子:
1
2
3
4
5
6
7
|
class Question(models.Model):
question_text = models.CharField(max_length = 200 )
pub_date = models.DateTimeField( 'date published' )
>>> Question.objects. all ()
>>> Question.objects.get(pk = 1 )
|
从例子可以看出,objects.all和objects.get这些功能都不是在class Question中定义的,可能在其父类models.Model中定义,也可能不是。那么我们在web.py中如何实现这样的功能呢?(如果你选择使用SQLAlchemy就不需要自己实现了)。
实现
思路
我们注意到Question.objects.all()这样的调用是直接访问了类属性objects,并调用了objects属性的方法all()。这里objects可能是一个实例,也可能是一个类。我个人认为(我没看过Django的实现)这应该是一个实例,因为实例化的过程可以传递一些表的信息,使得类似all()这样的函数可以工作。经过分析之后,我们可以列出我们需要解决的问题:
- 需要实现一个模型的父类Model,实际的表可以从这个父类继承以获得自己没有定义的功能。
- 实际的模型类(比如Question类)定义后,不实例话的情况下就要具备objects.all()这样的查询效果。
- 从上面的需求可以看出,我们需要在类定义的时候就实现这些功能,而不是等到类实例化的时候再实现这些功能。类定义的时候实现功能?这不就是metaclass(元类)做的事情嘛。因此实现过程大概是下面这样的:
- 实现一个Model类,其绑定方法和表的增、删、改有关。
- 修改Model类的元类为ModelMetaClass,该元类定义的过程中为类增加一个objects对象,该对象是一个ModelDefaultManager类的实例,实现了表的查询功能。
代码
都说不给代码就是耍流氓,我还是给吧。说明下:使用的数据库操作都是web.py的db库中的接口。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
|
# -*- coding: utf-8 -*-
import web
import config # 自定义的配置类,可以忽略
def _connect_to_db():
return web.database(dbn = "sqlite" , db = config.dbname)
def init_db():
db = _connect_to_db()
for statement in config.sql_statements:
db.query(statement)
class ModelError(Exception):
"""Exception raised by all models.
Attributes:
msg: Error message.
"""
def __init__( self , msg = ""):
self .msg = msg
def __str__( self ):
return "ModelError: %s" % self .msg
class ModelDefaultManager( object ):
"""ModelManager implements query functions against a model.
Attributes:
cls: The class to be managed.
"""
def __init__( self , cls ):
self . cls = cls
self ._table_name = cls .__name__.lower()
def all ( self ):
db = _connect_to_db()
results = db.select( self ._table_name)
return [ self . cls (x) for x in results]
def get( self , query_vars, where):
results = self . filter (query_vars, where, limit = 1 )
if len (results) > 0 :
return results[ 0 ]
else :
return None
def filter ( self , query_vars, where, limit = None ):
db = _connect_to_db()
try :
results = db.select( self ._table_name, vars = query_vars, where = where,
limit = limit)
except (Exception) as e:
raise ModelError( str (e))
return [ self . cls (x) for x in results]
class ModelMetaClass( type ):
def __new__( cls , classname, bases, attrs):
new_class = super (ModelMetaClass, cls ).__new__( cls , classname,
bases, attrs)
objects = ModelDefaultManager(new_class)
setattr (new_class, "objects" , objects)
return new_class
class Model( object ):
"""Parent class of all models.
"""
__metaclass__ = ModelMetaClass
def __init__( self ):
pass
def _table_name( self ):
return self .__class__.__name__.lower()
def insert( self , * * kargs):
db = _connect_to_db()
try :
with db.transaction():
db.insert( self ._table_name(), * * kargs)
except (Exception) as e:
raise ModelError( str (e))
def delete( self , where, using = None , vars = None ):
db = _connect_to_db()
try :
with db.transaction():
db.delete( self ._table_name(), where, vars = vars )
except (Exception) as e:
raise ModelError( str (e))
def save( self , where, vars = None , * * kargs):
db = _connect_to_db()
try :
with db.transaction():
db.update( self ._table_name(), where, vars , * * kargs)
except (Exception) as e:
raise ModelError( str (e))
|
使用
首先定义表对应的类:
1
2
|
class Users(Model):
...
|
使用就和Django的方式一样:
1
|
>>> user_list = Users.objects. all ()
|