orm

1.与之前项目的区别,保存数据使用json文件,项目使用数据库保存数据
如何将python数据和mysql创建联系
①类对应mysql的表,对象对应mysql的记录
如何创建类的时候自动帮我们创建表,使用到我们的元类
一切皆对象,对象是类产生的,类也是对象,元类就是产生类的类
当我们在python 中定义了一个类的时候会立即执行类中代码,触发了其元类的__init__方法
此方法需要三个参数,类名,类的父类们,类的名称空间
数据库创建表的sql语句 create table user(id int primary key auto_increment,name char(10));
现在我们需要知道表名,字段名和字段类型,约束条件
表名就是类名,字段名可以设置成类的类属性,这样定义类时就类属性会被解析到名称空间,我们就可以在其元类中__init__方法中拿到这些参数
因为字段还有类型和约束,故将字段设计为Filed类的对象
from orm.ykorm_tool import Mysql
class Mymeta(type):
    def __init__(self, cls_name, bases, namespace):
        columns = []
        pri_key = None
        for k, v in namespace.items():
            if isinstance(v, Field):
                fs = "%s %s" % (v.name, v.ctype)
                if v.pri_key:
                    if pri_key:
                        raise TypeError("主键重复")
                    pri_key = k
                    fs += " primary key auto_increment"
                if v.default != None:
                    fs += " default %s" % (v.default)
                columns.append(fs)
        if not pri_key:
            raise TypeError("未设置主键")
        self.pri_key =pri_key
        cs = ",".join(columns)
        sql = "create table %s(%s)" % (cls_name.lower(), cs)
        Mysql.create_table(sql)

class Field:
    def __init__(self, name, ctype, default=None, pri_key=False):
        self.name = name
        self.ctype = ctype
        self.default = default
        self.pri_key = pri_key

class Intfield(Field):
    def __init__(self,name,ctype="int",default=None,pri_key=False):
        super().__init__(name,ctype,default,pri_key)

class Strfield(Field):
    def __init__(self,name,ctype="varchar(200)",default=None,pri_key=False):
        super().__init__(name,ctype,default,pri_key)

class History(metaclass=Mymeta):#当我们写这行时,会自动触发Mymeta.__init__(self,cls_name,bases,namespace)方法
    id = Intfield("id", pri_key=True)
    movie_id= Intfield("movie_id")
    user_id = Intfield("user_id")
    view_time = Strfield("view_time", "timestamp")

    def __init__(self, movie_id, user_id):
        self.movie_id = movie_id
        self.user_id = user_id

②表创建完成后,需要通过数据库帮我们将项目产生的数据保存在对应的表中
需要使用pymysql模块来使用python操作数据库

import pymysql
import time
from conf.settings import MYSQL_CONF
#创建连接
def create_conn(obj):
    conn = pymysql.connect(
        host = MYSQL_CONF["host"],
        user = MYSQL_CONF["user"],
        passwd = MYSQL_CONF["passwd"],
        db = MYSQL_CONF["db"],
        autocommit = MYSQL_CONF["autocommit"],)
    obj.current_conn += 1
    obj.pools.append(conn)

#定义连接类,可以设置最大连接数,保证数据库不会崩
class Conn:
    pools = []
    current_conn = 0
    def __init__(self,max = 10,retry_time = 0.2):
        self.max = max
        self.retry_time = retry_time
        for i in range(5):
            create_conn(self)

    def execute(self,sql,args=None,isselect=False):
        if not self.pools:
            if self.current_conn < self.max:
                create_conn(self)
            else:
                time.sleep(self.retry_time)
                return self.execute(sql,args,isselect)
        conn = self.pools.pop()
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        res = 0
        try:
            res = cursor.execute(sql,args)
        except Exception as e:
            print(e)
        if isselect:
            res = cursor.fetchall()
        self.pools.append(conn)
        return res

    def select(self,sql,args=None):
        return self.execute(sql,args,isselect=True)

③优化了连接问题,需要使用数据库帮我们增删改查数据了
定义一个Mysql工具类,实现通过主键的增删改查,和通过条件的查询

from orm.ykorm_conn import Conn

class Mysql:
    __conn = Conn()

    @classmethod
    def create_table(cls,sql):
        return cls.__conn.execute(sql)

    @classmethod
    def save(cls,obj):
        columns = []
        values = []
        for k,v in obj.__dict__.items():
            columns.append(k)
            values.append(v)
        cs = ",".join(columns)
        vs = ",".join(["%s" for i in values])
        sql = "insert into %s(%s)values(%s)"%(obj.__class__.__name__,cs,vs)
        return cls.__conn.execute(sql,values)


    @classmethod
    def get(cls,cls_name,id):
        sql = "select * from %s where %s = %s"%(cls_name.__name__,cls_name.pri_key,"%s")
        res = cls.__conn.select(sql,id)
        if not res:return
        obj = object.__new__(cls_name)
        obj.__dict__ = res[0]
        return obj

    @classmethod
    def update(cls,obj):
        columns = []
        values = []
        for k,v in obj.__dict__.items():
            fs ="%s = "%k
            fs += "%s"
            columns.append(fs)
            values.append(v)
        cs = ",".join(columns)
        values.append(obj.__dict__[obj.pri_key])
        sql = "update %s set %s where %s = %s"%(obj.__class__.__name__,cs,obj.pri_key,"%s")
        return cls.__conn.execute(sql,values)

    @classmethod
    def delete(cls,obj):
        sql = "delete from %s where %s = %s"%(obj.__class__.__name__,obj.pri_key,obj.__dict__[obj.pri_key])
        return cls.__conn.execute(sql)


    @classmethod
    def get_many(cls,cls_name,conditions=None,limit=None):
        sql = "select * from %s"%cls_name.__name__
        if conditions:
            sql += " where %s"%conditions
        if limit:
            if isinstance(limit,int):
                sql += "limit %s"%limit
            elif isinstance(limit,tuple):
                sql += "limit %s,%s"%(limit[0],limit[1])
            else:
                raise TypeError("limit must be int or tuple")
        res =  cls.__conn.select(sql)
        if not res:return
        objs = []
        for dic in res:
            obj = object.__new__(cls_name)
            obj.__dict__ = dic
            objs.append(obj)
        return objs

以上方法都是类方法,不用产生对象,直接使用MYSQL点方法名,若设置为绑定方法的话,需要将其产生的对象设置为单例模式,因为大家的数据库连接配置都是相同的

posted @ 2019-02-24 16:20  robertzhou  阅读(216)  评论(0编辑  收藏  举报