Getting started with writing an ORM framework in Python
This article mainly introduces how to compile the ORM framework in Python. The sample code is based on Python2.x. For more information, see
With the db module, It is very convenient to directly write SQL statements to the database. However, we still lack ORM. If you have an ORM, you can use a statement like this to obtain the User object:
?
|
1 |
User = User. get ('20140901 ')
|
Instead of writing SQL statements, convert them to User objects:
?
|
1
2 |
U = db. select_one ('select * from users where id =? ', '123 ')
User = User (** u)
|
So we started to write the ORM module: transwarp. orm.
Design An ORM Interface
Similar to designing a db module, designing an ORM is also designed from the perspective of upper-layer callers.
We first consider how to define a User object, and then associate the database table users with it.
?
|
1
2
3
4
5
6 |
From transwarp. orm import Model, StringField, IntegerField
Class User (Model ):
_ Table _ = 'users'
Id = IntegerField (primary_key = True)
Name = StringField ()
|
Note that _ table _, id, and name defined in the User class are the attributes of the class, not the attributes of the instance. Therefore, attributes defined at the class level are used to describe the User ing between the User object and the table, and instance attributes must be initialized using the _ init _ () method, so they do not interfere with each other:
?
|
1
2
3
4 |
# Create an instance:
User = User (id = 123, name = 'Michael ')
# Database storage:
User. insert ()
|
Implement the ORM Module
With the definition, we can start to implement the ORM module.
The first thing to define is the base class Model of all ORM ing:
?
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14 |
Class Model (dict ):
_ Metaclass _ = ModelMetaclass
Def _ init _ (self, ** kw ):
Super (Model, self). _ init _ (** kw)
Def _ getattr _ (self, key ):
Try:
Return self [key]
Failed t KeyError:
Raise AttributeError (r "'dict 'object has no attribute '% S'" % key)
Def _ setattr _ (self, key, value ):
Self [key] = value
|
Model inherits from dict, so it has all dict functions and implements special methods _ getattr _ () and _ setattr __(), therefore, you can write data as if you reference a common field:
?
|
1
2
3
4 |
>>> User ['id']
123
>>> User. id
123
|
Model is only a base class. How can we read the ing information of specific sub-classes such as users? The answer is through metaclass: ModelMetaclass:
?
|
1
2
3
4
5
6
7
8
9
10 |
Class ModelMetaclass (type ):
Def _ new _ (cls, name, bases, attrs ):
Mapping =... # Read the cls Field
Primary_key =... # search for the primary_key Field
_ Table _ = cls. _ talbe _ # Read the _ table _ field of cls.
# Add some fields to cls:
Attrs ['_ mapping _'] = mapping
Attrs ['_ primary_key _'] = _ primary_key __
Attrs ['_ table _'] = _ table __
Return type. _ new _ (cls, name, bases, attrs)
|
In this way, any class (such as User) inherited from the Model will automatically scan the ing relationship through ModelMetaclass and store it in its own class.
Then, we add the class method to the Model class so that all subclasses can call the class method:
?
|
1
2
3
4
5
6
7
8 |
Class Model (dict ):
...
@ Classmethod
Def get (cls, pk ):
D = db. select_one ('select * from % s where % s =? '% (Cls. _ table __, cls. _ primary_key _. name), pk)
Return cls (** d) if d else None
|
The User class can be used to find the primary key through the class method:
User = User. get ('20140901 ')
To add an instance method to the Model class, you can have all subclass call the instance method:
?
|
1
2
3
4
5
6
7
8
9
10 |
Class Model (dict ):
...
Def insert (self ):
Params = {}
For k, v in self. _ mappings _. iteritems ():
Params [v. name] = getattr (self, k)
Db. insert (self. _ table __, ** params)
Return self
|
In this way, you can save a User instance to the database:
?
|
1
2 |
User = User (id = 123, name = 'Michael ')
User. insert ()
|
The last step is to improve the ORM. We can implement the following methods for searching:
?
|
1
2
3
4
5 |
Find_first ()
Find_all ()
Find_by ()
|
For count, you can:
?
|
1
2
3 |
Count_all ()
Count_by ()
|
And the update () and delete () methods.
Finally, let's take a look at how many lines of code are implemented in the ORM module? Only over 300 lines are added with comments and doctest. Is it easy to write an ORM using Python?