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点方法名,若设置为绑定方法的话,需要将其产生的对象设置为单例模式,因为大家的数据库连接配置都是相同的