Looking at the python object creation process through the ORM model

A simple django ORM model looks like this:

#!/usr/bin/env python
# encoding: utf-8
"""
@version: 1.0
@author: Pis
@license: Apache Licence
@software: PyCharm
@file: orm_test.py
@time: 2018/5/9 9:52
"""

#1
class Field(object):
    def __init__(self, name, column_type):
        self.name = name
        self.column_type = column_type

    def __str__(self):
        return '<%s:%s>' % (self.__class__.__name__, self.name)

#2
class StringField(Field):
    def __init__(self, name):
        super(StringField, self).__init__(name, 'varchar(100)')

#3
class IntegerField(Field):
    def __init__(self, name):
        super(IntegerField, self).__init__(name, 'bigint')

#4
# The next step is to write the most complex ModelMetaclass:
class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        if name == 'Model': # exclude modifications to the Model class;
            return type.__new__(cls, name, bases, attrs)
        print('Found Model: %s' % name)
        mappings = dict()
        for k, v in attrs.items(): # Find all attributes of the defined class,
            if isinstance(v, Field): # If a Field property is found,
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v # save it to a dict of __mappings__
        for k in mappings.keys():
            attrs.pop(k) # At the same time, delete the Field attribute from the class attribute, otherwise, it is easy to cause a runtime error (the attribute of the instance will cover the attribute of the same name of the class);
        attrs['__mappings__'] = mappings # Save the mapping between attributes and columns
        attrs['__table__'] = name # Assume the table name is the same as the class name and save the table name to __table__
        return type.__new__(cls, name, bases, attrs)

#5
# base class Model
class Model(dict, metaclass=ModelMetaclass):
    def __init__(self, **kw):
        super(Model, self).__init__(**kw)

    def __getattr__(self, item): # attribute not found, look for it here
        try:
            return self[item]
        except KeyError:
            raise AttributeError(r"'Model' object has no attrs :'%s'" % item)

    def __setattr__(self, key, value):
        self[key] = value

    def save(self):
        fields = []
        params = []
        args = []
        for k, v in self.__mappings__.items():
            fields.append(v.name)
            params.append('?')
            args.append(getattr(self, k, None))
        sql = 'insert into %s (%s) values (%s)' % (self.__table__, ','.join(fields), ','.join(params))
        print('SQL: %s' % sql)
        print('ARGS: %s' % str(args))

    # Subclass User

#6 # Define the mapping of class attributes to columns: class User(Model): id = IntegerField('id') name = StringField('username') email = StringField('email') password = StringField('password') age = IntegerField('age')
#7 if __name__ == "__main__": u = User(id=12345, name='john', email='[email protected]', password='666666', height="top") u.age = 12 u.save()

 

In the process of code execution, before executing #7, the first step is to create a class. If metaclass is not indicated, the class object is created by type through __new__ by default, and if metaclass is indicated, the class object is created by __new__ of metaclass Method creation (the search order of metaclass is given in the previous article [self, parents, local]), the execution of #7 starts after the class is created, because the User class does not construct __new__ and __init__ methods, and the User's The parent class Model inherits the dict class, so when the height field is not in User's attrs, no error will be reported. In fact, any keyword parameters will not be wrong. When the ORM model is in u.save(), it searches for the fields in __map__ in __new__, that is, (id, name, email, password, age) in #6, and dynamically executes the storage through sql statements operate.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325950366&siteId=291194637