跳过正文

利用元编程的技巧,让sqlalchemy能够动态更换表名

·98 字·1 分钟
Remy
作者
Remy
Bug的设计师,故障的制造机,P0的背锅侠。代码里的隐秘问题总能被我创造性地解锁。写代码如同解谜,有时谜底是惊喜,有时是惊吓。

问题
#

sqlalchemy是一个class对应一张表,但是,如果我们有这样一个需求,分表的需求,此时,我们表结构一样,但是我们表名不一样, 这样的好处是,我们将很大的数据分成小的不同的表,使得我们有更好的查询速度和查询效率,但是问题就这么来了,我们该如何动态的 更新表名呢?最蠢得办法是也定义不同的class去映射相映的分表。不好,并不优雅

解决方案
#

  1. 我们动态的更新表名,只需要提前设置__tablename__的属性即可,那么,我们怎么做呢?
  • 首先,我们定义一个class去映射
class User(db.Model):
    __tablename__ = 'User'
    id = db.Column(db.Interger,primary_key=True)
    user_name = db.Column(db.String(50))
  • 然后我们每次调用他之前更改他的表名

    User.__table__.name = 'user_1'
    
  • 这时我们就可以用这个User类去查询了

    User.query.filter(User.id = 1).first()
    

    缺陷
    #

    然而不幸的是,这总方法查询是没问题,但是如果我们插入或者我们需要查询多张表的时候,他只能认第一次设置的表明,这就麻烦了 但是,依然是有解决方法的,而且更加的优雅!

终极解决方案
#

我们知道,python是动态语言,我们也知道,class也是一个对象,那么我们只要动态的创建一个class,再利用这个class去查询就可以 优雅的解决了。

  • 首先,我们定义一个对象,并且让他能够动态的得到我们想要的表明的类,

    class User(object):
      _mapper = {}
      @staticmethod
      def model(id):
          table_name = 'user_%s' %(id)
          ModelClass = User._mapper.get(class_name)
          if ModelClass is None:
              ModelClass = type(class_name,(db.Model,),{
                  '__module__' : __name__,
                  '__name__' : class_name,
                  '___tablename__':table_name,
                  'id' : db.Column(db.Integer, primary_key=True),
                  'user_name' : db.Column(db.String(50)),
              })
          User._mapper[class_name] = ModelClass
          cls = ModelClass()
          return cls
    

    此时我们就可以用这个方法返回的类去查询和插入都可以

    # 查询
    user_model = User.model(1)
    user_model.query.filter(user_model.id = 1).one()
    # 插入
    user_model.user_name = "赵四"
    db.session.add(user_model)
    db.session.commit