`Scenario 2021-02-16 16:49 采纳率: 0%
浏览 201
已结题

大家好ORM框架到cursor.execute(query,args)报错了求大佬帮忙看看要怎么修改

import pymysql

###-------------------属性描述器-------------------###
class Field:   #描述器(类型判断) int  varchar  date  enum  float
    def __init__(self,name,fieldname=None,pk=False,unique=False,default=None,nullable=True,index=False):
        self.name = name
        if fieldname is None:         #字段名为空则将传入name当做字段名
            self.fieldname = name
        else:
            self.fieldname =fieldname
        self.pk = pk
        self.unique = unique
        self.default = default
        self.nullable = nullable
        self.index = index

    def validate(self,value):   #数据校验  分不同类型进行
        raise NotImplementedError

    def __get__(self, instance, owner):  #instance是创建字段student的实例
        if instance is None:             #描述器的get返回的就是字段名--值的字典
            return self
        return instance.__dict__[self.name]

    def __set__(self, instance, value):  #设置字段的值  字段名--值
        self.validate(value)         #set之前先校验数据
        instance.__dict__[self.name] = value   #给对应属性名设置值



    def __str__(self):     #字段实例的本身是哪个  做区分
        return "< {}类型的{}字段 >".format(self.__class__.__name__ , self.name)
    __repr__ = __str__

class IntField(Field):
    def __init__(self,name,fieldname=None,pk=False,unique=False,default=None,nullable=True,auto_increment=False,index=False):
        self.auto_increment = auto_increment  #int仅有
        super().__init__(name,fieldname,pk,unique,default,nullable,index)

    def validate(self,value):   #auto_increment
        if value is None:
            if self.pk:        #主键不可为None
                raise TypeError(u"作为主键的%s 的属性%s 的值%s 异常" % (self.pk,self.name,value))
            if not self.nullable: #如果当前为空  但数据库不允许为空时
                raise TypeError(u"%s required" % self.name)
        else:
            if not isinstance(value,int):  #判断是否为int型数据
                raise TypeError(u"%s should be int" % self.name)



class StringField(Field):   #字符串需要一个长度属性
    def __init__(self,name,length=32,fieldname=None,pk=False,unique=False,default=None,nullable=True,index=False):
        self.length = length
        super().__init__(name,fieldname,pk,unique,default,nullable,index)

    def validate(self,value):
        if value is None:
            if self.pk:        #主键不可为None
                raise (u"作为主键的%s 的属性%s 的值%s 异常" % (self.pk,self.name,value))
            if not self.nullable: #如果当前为空  但数据库不允许为空时
                raise TypeError(u"%s required"% self.name)
        else:
            if not isinstance(value,str):  #判断是否为int型数据
                raise TypeError(u"%s should be str"% self.name)
            if len(value) > self.length:
                raise ValueError(u"字段%s 长度超出范围,value=%s"% (self.name,value) )


###-------------------数据库操作会话 cursor操作的类-------------------###
class Session():    #这种模式会造成线程不安全 有一定风险 但Session应该不跨线程 全局使用
    def __init__(self, conn):#:pymysql.connections.Connection):
        self.conn = conn
        self.cursor = None
        print("初始化完成")


    def execute(self, query,*args):   #SQL执行通用方法  query参数接收SQL语句
        #连接数据库 执行SQL语句      数据库的连接由外部传入,应是全局变量
        if self.cursor is None:   #判断保护 如果没有游标则新建一个
            self.cursor = self.conn.cursor()   #指令参数化 传入的是元组
            print("已建立游标")
        print("已存在游标")
        self.cursor.execute(query,args)



    def __enter__(self):   #with Session实例操作时,单独获取一个游标
        self.cursor = self.conn.cursor()
        return  self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:   #有异常 回滚
            self.conn.rollback()
        else:           #无异常 执行
            self.conn.commit()
        self.cursor.close()


###-------------------元类式反射  定义实体类的模板-------------------###
class ModelMeta(type):   #元类只适合放共性相同的属性和方法
    def __new__(cls, name:str, bases,attrs:dict):       #attrs是 属性-值 的字典
        #解决表名的问题 给了直接用  没给 就用name首字母大写作为表名
        if attrs.get('__tablename__',None)is None:    #获取表名 __tablename__
            attrs['__tablename__'] = name.lower()

        mapping = {}  #映射字典
        primarykey = []  #列表保存主键 主键不止一个!
        for k,v in attrs.items():   #k属性名  v属性所对应的字段类型  保存在mapping字典中
            if isinstance(v,Field):
                mapping[k] = v        #注意:这里在类构建时已经执行
                if v.name is None:
                    v.name = k
                if v.fieldname is None:
                    v.fieldname = v.name
                if v.pk:
                    primarykey.append(v)

        attrs['__mapping__'] = mapping  #未定义时已经把字段属性放到映射字典中保存 作为__mapping__属性
        attrs['primarykey'] = primarykey

        return super().__new__(cls, name, bases,attrs)
"""
session.execute(sql , values)
将sql中的属性名(表名,字段名...)分离出来
values保存对应要添加的值
names保存属性名
"""

class Model(metaclass=ModelMeta): pass  #基类来统一数据库操作

#实体类
class Engine:
    def __init__(self,*args , **kwargs):
        self.conn = pymysql.connect(*args , **kwargs)

    def save(self,instance):
        names = []
        values = []
        for k,v in instance.__mapping__.items():   #注意:当Model子类的实例调用方法时才调用
            if isinstance(v,Field):  #过滤出Field实例化的v
                names.append(k)    #k是属性名     访问v方式:
                values.append(instance.__dict__[k])  #v是属性名对应的Field  访问v方式:__dict__[k]
            #sql = "insert into student(id,name,age)values(%s,%s,%s)"

        sql = "insert into {}({}) values ({})".format(instance.__tablename__ ,",".join(names), ",".join(['%s']*len(names)))   #把values后面的参数拼成列表的形式[%s,%s,%s]
        print(sql)
        print(values)

        session = Session(self.conn)
        with session:
            session.execute(sql, values)


###-------------------数据库连接和操作  实体类-------------------###
class Student(Model):   #操作表student的记录
    __tablename__ = "student"
    id = IntField('id', 'id',True,nullable=False,auto_increment=True)#用描述器来指定属性名称(字段名) 按照int字段来创建 name fieldname pk null ...
    name = StringField('name',length=64,nullable=False)
    age = IntField('age')        #默认age不做pk 默认就为False

    def __init__(self,id,name,age):
        self.tablename = ""
        self.id = id     #调用__set__方法  instance即为当前实例   字典的key为属性名称
        self.name = name
        self.age = age


    # def __str__(self):
    #     return "Student(%s,%s,%s)" % (self.id,self.name,self.age)



if  __name__  == '__main__':
    s = Student(int(1),'tom',int(20))
    engine = Engine(host = "10.0.0.6",port = 3306,user = "root",password='123',db = 'db1',charset='utf8')
    engine.save(s)

报错:  File "D:/Program Files/python code/数据分析/ORM关系映射操作MySQL/01ORM基本框架.py", line 100, in execute
    self.cursor.execute(query,args)
  File "D:\Program Files\Python\lib\site-packages\pymysql\cursors.py", line 146, in execute
    query = self.mogrify(query, args)
  File "D:\Program Files\Python\lib\site-packages\pymysql\cursors.py", line 125, in mogrify
    query = query % self._escape_args(args, conn)
TypeError: not enough arguments for format string

  • 写回答

1条回答 默认 最新

  • 幻灰龙 2021-02-16 23:16
    关注

    >TypeError: not enough arguments for format string

    应该是sql格式化字符串里的占位符和传入的参数数组个数不匹配,打印出来,贴出来看下就知道

    sql = "insert into {}({}) values ({})".format(instance.__tablename__ ,",".join(names), ",".join(['%s']*len(names))) 
    
    print(sql) 
    
    print(values)
    评论

报告相同问题?

悬赏问题

  • ¥15 微信会员卡接入微信支付商户号收款
  • ¥15 如何获取烟草零售终端数据
  • ¥15 数学建模招标中位数问题
  • ¥15 phython路径名过长报错 不知道什么问题
  • ¥15 深度学习中模型转换该怎么实现
  • ¥15 HLs设计手写数字识别程序编译通不过
  • ¥15 Stata外部命令安装问题求帮助!
  • ¥15 从键盘随机输入A-H中的一串字符串,用七段数码管方法进行绘制。提交代码及运行截图。
  • ¥15 TYPCE母转母,插入认方向
  • ¥15 如何用python向钉钉机器人发送可以放大的图片?