Python -- how to implement ORM with metaclasses

Metaclass implementation ORM

ORM is the core idea of Django, "Object Relational Mapping", that is, Object Relational Mapping, which is used to simplify SQL operations, encapsulate database operations into classes, map table names into classes, map fields into properties, and map rows (data) into instances.

class ModelMetaclass(type):
    def __new__(cls, name, bases, attrs):
        mappings = dict()
        # Determine whether to save
        for k, v in attrs.items():
            # Determine whether it is an instance object of the specified StringField or IntegerField
            if isinstance(v, tuple):
                print('Found mapping: %s ==> %s' % (k, v))
                mappings[k] = v

        # Delete these properties that are already stored in the dictionary
        for k in mappings.keys():
            attrs.pop(k)

        # Add the previous uid/name/email/password and the corresponding object reference and class name
        attrs['__mappings__'] = mappings  # Save mapping between attributes and columns
        attrs['__table__'] = name  # Assume that the table name and class name are the same
        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)")
    # When a metaclass is specified, the above class properties are not stored in the class, but in the dictionary specified by the mappings property
    # There are 
    # __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:
            # Judge in if it is a number type
            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='test@orm.org', password='my-pwd')
# print(u.__dict__)
u.save()

Note:

  • Setattr (object, property, property value) sets the value of the property in the object
  • Getattr (object, property [, default]) reads the value of the property from the object. You can set the default value, or read the default value if it does not exist.

Tags: Programming SQL Django Database

Posted on Tue, 05 Nov 2019 12:48:17 -0500 by tstout2