Peewee 模型中的 Getter 和 setter 方法

Getter and setter methods in a Peewee model

我在 Peewee 中有以下简单模型:

class SiteText(BaseModel):
    url = TextField()
    text = TextField()
    my_counter = IntegerField()

    def get_text_by_url(url):
        d = [s.text for s in SiteText.select(SiteText.url == url)]
        d = d[0] if len(d) > 0 else None
        return d

    def save_text(updates):
        # updates is a dict containing, url, text, my_counter
        SiteText.upsert(**updates)

    def get_outage_counter(url):
        c = [c.my_counter for c in SiteText.select(SiteText.url == url)]
        c = c[0] if len(c) > 0 else None
        return c

    def set_outage_counter(url, counter):
        c = SiteText.get(SiteText.url == url)
        c.counter = counter
        c.save()

但是有些属性写getter和setter感觉比较奇怪。有没有更Pythonic的方式来做到这一点?例如,我是否应该使用一种方法来获取和设置指定 URL 的中断计数器?我是否应该将 getter 和 setter 函数转换为属性(尽管它们会与实际属性发生冲突)。欢迎反馈!

实际上使用属性不会与您的属性名称冲突,因为它们可能与 getter/setter 函数不同。

class Example:
    def __init__(self):
       self.value = 5

    @property
    def value(self):
        print('Entering value getter')
        return self._value

    @value.setter
    def value(self, val):
        print('Entering value setter')
        self._value = val

假设我们有上面的 class。您可以看到 getter 函数正在返回 _value 这对于那些在 __init__ 方法中看到只有 self.value = 5 而没有 self._value 的人来说可能会很奇怪].

假设我们接下来要这样做:

a = Example()

在实例化 class 对象之后,由于 self.value = 5__init__ 正在调用 value.setter 函数,因此,我们得到:

a = Example()
Entering value setter
print(a.value)
Entering value getter
5
print(a.__dict__)
{'_value': 5} # _value was created in setter function

因此,如您所见,您可以这样使用属性,并且没有问题。

由于 peewee 使用 metaclasses 和 class 变量的方式,无法在 __init__ 中定义您的字段,您必须替换名称

class SiteText(db.Model):
    url = CharField()    

    @property
    def gs_url(self):
        return self.url

    @gs_url.setter
    def gs_url(self, val):
        self.url = val

Peewee 的 playhouse 扩展包括 hybrid attributes,这可能正是您要找的。它们基本上是 @properties,但经过修改以与 peewee 一起使用。

这是我自己的代码示例:

from peewee import *
from playhouse import hybrid    # <-- imports the hybrid extension

class User(BaseModel):
    username = CharField()
    _password = CharField()    # <-- note the underscore

    # ... other fields come here ...

    @hybrid.hybrid_property
    def password(self):
        return self._password

    @password.setter
    def set_password(self, plaintext):
        self._password = generate_password_hash(plaintext)

这是为了存储散列密码:因此 setter 使用函数 (generate_password_hash) 在存储密码之前对其进行处理。

在您的情况下,您将改用自己的函数

class SiteText(BaseModel):
    _counter = IntegerField()

    @hybrid.property
    def counter(self):
        # code to process output can come here
        return _counter

    @counter.setter
    def set_counter(self, counter):
        # code to process counter comes here
        self._counter = counter

然后,如果 s 是一个 SiteData 对象,您可以 运行 命令,如 s.counter = 1print s.counter 等。在将数据保存为对象中的 _counter 之前,将使用 getter 和 setter 自动预处理数据。

最后,我建议您不要在 getter 和 setter 中 运行 save()。相反,让属性保存在对象中,然后手动 运行 save() 将它们提交到数据库。否则你最终可能会不必要地多次访问数据库,或者保存你不想要的东西。