peewee:重用动态创建的模型

时间:2014-11-18 17:25:43

标签: python-2.7 peewee

在我的应用程序中,我有一个模型,我只知道运行时的列数。使用下面的Factory之类的函数来创建模型很好地解决了这个问题。但是,如果我多次使用它(可能会有不同的字段),则创建外键ref会引发异常:

AttributeError: Foreign key: dynamictable.ref related name "dynamictable_set" 
collision with foreign key using same related_name.

消息非常清楚,当我在创建外键时设置related_name参数时,没有错误。

问题

  1. 为什么我第二次不能使用相同的related_name?我是否还需要重新定义StaticTable

  2. 是否有更好的方法可以使用动态模型写入多个数据库?

  3. 最小,可重复的例子:

    import peewee
    
    database_proxy = peewee.Proxy()
    
    class BaseModel(peewee.Model):
        class Meta:
            database = database_proxy
    
    class StaticTable(BaseModel):
        foo = peewee.DoubleField()
    
    def Factory(fields):
        class DynamicTable(BaseModel):
            ref = peewee.ForeignKeyField(StaticTable)
        for field in fields:
            peewee.DoubleField().add_to_class(DynamicTable, field)
        return DynamicTable 
    
    def Test(fname, fields):
        db = peewee.SqliteDatabase(fname)
        database_proxy.initialize(db)
        db.create_table(StaticTable)
        dyntable = Factory(fields)
        db.create_table(dyntable)
        db.close()
    
    
    Test(':memory:', ['foo', 'bar'])
    Test(':memory:', ['foo', 'bar', 'extra'])
    

2 个答案:

答案 0 :(得分:4)

我认为这突出了peewee中的一个错误,你可能明确地想要忽略任何backrefs。我打开了一张票并将解决它。

https://github.com/coleifer/peewee/issues/465

与此同时,您可以通过在模型上设置动态related_name来消除错误,例如

def Factory(fields):
    dynamic_name = '_'.join(fields)
    class DynamicTable(BaseModel):
        ref = peewee.ForeignKeyField(StaticTable, related_name=dynamic_name)
    for field in fields:
        peewee.DoubleField().add_to_class(DynamicTable, field)
        return DynamicTable

更新:根据#465中的修复,现在可以禁用反射验证:

def Factory(fields):
    class DynamicTable(BaseModel):
        ref = peewee.ForeignKeyField(StaticTable, related_name=dynamic_name)
        class Meta:
            validate_backrefs = False
    for field in fields:
        peewee.DoubleField().add_to_class(DynamicTable, field)
        return DynamicTable

答案 1 :(得分:0)

@coleifer的建议(在他的回答中)效果很好,只要字段列表是唯一的。由于在我的情况下无法保证,我提出了这个稍微复杂的解决方案,其中所有模型类都在工厂中创建:

import peewee

database_proxy = peewee.Proxy()

def orm_factory():

    class OrmWrapper:
        class BaseModel(peewee.Model):
            class Meta:
                database = database_proxy

        class StaticTable(BaseModel):
            foo = peewee.DoubleField()

        @classmethod
        def dyntable_factory(cls,fields):
            class DynamicTable(cls.BaseModel):
                ref = peewee.ForeignKeyField(cls.StaticTable)
            for field in fields:
                peewee.DoubleField().add_to_class(DynamicTable, field)
            return DynamicTable

    return OrmWrapper

def test(fname, fields):
    orm = orm_factory()
    db = peewee.SqliteDatabase(fname)
    database_proxy.initialize(db)
    db.create_table(orm.StaticTable)
    dyntable = orm.dyntable_factory(fields)
    db.create_table(dyntable)
    db.close()

test(':memory:', ['foo', 'bar'])
test(':memory:', ['foo', 'bar'])