在模型上使用查询类方法时,如何避免Peewee中的循环导入?

问题描述 投票:0回答:1

当使用Peewee时,我遵循的建议是 在Peewee Models Python中创建 "查询方法" 答:我为所有的查询创建了类方法。

class Person(Model):
    name = CharField()
    age = IntegerField()

    @classmethod
    def adults(cls):
        return cls.select().where(cls.age > 18)

我为我的所有查询创建类方法,以保持我的模型 "胖",其他的都 "瘦"。现在我引入了一个外键,我很苦恼这个方法,因为Peewee要求我在查询中直接使用模型类。

class Metric(Model):
    person = ForeignKeyField(Person, backref='metrics')
    name = CharField()
    value = IntegerField()

Other file:

class Person(Model):
    name = CharField()
    age = IntegerField()

    @classmethod
    def popular(cls, min_likes):
        return cls.select(cls, Metric).join(Metric).where(Metric.name == 'likes', Metric.value > min_likes)

这行不通,因为 Metric 定义取决于 Person 反之亦然。循环导入. 文件中有一节 循环的外键依赖性,其中类似情况的解决方案是 DeferredForeignKey但我不喜欢它,因为它增加了代码中的开销(到处都需要手动创建外键),而且因为我的应用程序使用的是SQLite--文档中明确指出了以下内容。

因为SQLite对改变表的支持有限,所以在表被创建后,不能向表添加外键约束。

如果我的理解正确的话,这实际上意味着我将完全失去FK约束。不过我想要这个约束,应用程序依靠的是缺少对应的记录会抛出异常。

是否有我忽略的不同的变通方法?有这样的胖模型毕竟是Peewee的推荐做法吗?我很喜欢,但这让我的模型设计陷入了死胡同。文档中甚至说。

我个人的观点是,循环外键是一种代码气味,应该重构(例如,通过添加一个中间表)。


更新了一下。 我更新了问题,因为原来我无意中漏掉了主要细节。我在处理循环外键 进口不仅仅是 依赖性 类之间的名称。如果我把这些类拼接到一个文件中,那就可以了,因为Python只在调用classmethods时才会解析它们的名字,但这不是我要解决的问题,我想把这些类放在不同的模块中。

python sqlite foreign-keys peewee
1个回答
0
投票

我认为你不了解Python的作用域。在方法体中引用一个相关的模型并没有错,例如。

# Move metric below Person.
class Person(Model):
    name = CharField()
    age = IntegerField()

    @classmethod
    def popular(cls, min_likes):
        return cls.select(cls, Metric).join(Metric).where(Metric.name == 'likes', Metric.value > min_likes)


class Metric(Model):
    person = ForeignKeyField(Person, backref='metrics')
    name = CharField()
    value = IntegerField()

或者你可以使用 DeferredForeignKey 所建 恰恰 为此。


0
投票

我想出了一个丑陋的变通方法。为了完整起见,我把它作为自己问题的答案贴出来,但我不喜欢这个解决方案,所以我不接受它作为答案。

鉴于包的结构是这样的。

models/
    __init__.py
    person.py
    metric.py

而鉴于... __init__.py 看起来像这样。

from .person import Person
from .metric import Metric

允许简化导入。from models import Person 而不是 from models.person import Person

那么变通的办法可能是一个丑陋的第三个文件,里面只有其中一个模型的属性。比如说 person_attrs.py:

models/
    __init__.py
    person.py
    person_attrs.py
    metric.py

这些文件的内容如下。个人:

class Person(Model):
    name = CharField()
    age = IntegerField()

Metric:

from .person import Person

class Metric(Model):
    person = ForeignKeyField(Person, backref='metrics')
    name = CharField()
    value = IntegerField()

人物属性。

from .metric import Metric

__all__ = ['popular']

@classmethod
def popular(cls, min_likes):
    return cls.select(cls, Metric).join(Metric).where(Metric.name == 'likes', Metric.value > min_likes)

然后是 __init__.py 作为胶水工作。

from .person import Person
from .metrics import Metric


from . import person_attrs  # isort:skip

for attr_name in person_attrs.__all__:
    setattr(Person, attr_name, getattr(person_attrs, attr_name))

这个变通方法滥用了 __all__ 但我觉得还是显式的好,比用隐式算法查找属性要好),还有就是导入顺序在 __init__.py 变得重要。该 person_attrs.py 允许同时定义 @classmethod@property 方法,现在可以使用其他模型 Metric 随意自由,但代价是作为顶层对象被驱逐到一个单独的档案中,只能与上层对象联合起来 Person 榜样 models 包根。

显然不好看,不直接,挺丑的,但我除了把模型放在一个文件里,还想不出别的办法。

© www.soinside.com 2019 - 2024. All rights reserved.