【问题标题】:Django models with varied types of functionality具有不同类型功能的 Django 模型
【发布时间】:2017-03-29 22:18:28
【问题描述】:

我有一个用例,我想在 Django 应用程序中为用户(由业务逻辑自动生成)捕获各种类型的提醒。在所有提醒中,属性相同(即ownercreatorcreation_timestatuscompletion_time)并且它们具有相同的接口(即render_message()is_valid())。但是,提醒的类型有很多种,对于这些常用功能,每种提醒都应该有自己的实现细节。

由于模型属性永远不会改变,因此使用单个 Django 模型似乎是有意义的,但是我质疑从模型实例到函数的具体实现的最佳方式,例如 render_message()

我目前正在考虑在 Reminder 模型上添加一个名为 typeChoiceField,它将映射到各种提醒类型类,然后在 Reminder 中有一个帮助函数,它将实例化适当的提醒类型类基于self.type 并将其称为render_message() 之类的功能。

我错过了更好的方法吗?不知道这种模式被认为是什么,或者在哪里可以找到具有与同一接口的各种实现相关联的单个 Django 模型的类似示例。

【问题讨论】:

  • 您能否举例说明render_message 的各种实现有何不同?
  • 您可以使用class Meta: abstract = True 甚至@abc.abstractmethod,但我从来不需要这些Javaism。如果您正在处理始终具有 render_message() 的提醒,只需调用它即可。如果 EatReminder 有不同的 render_message,覆盖它。
  • @AntoinePinsard 每个提醒都与一个项目对象相关联。一种提醒类型可能是过期项目,因此 render_message 将返回 "<project_name> is over due by x days"。另一种提醒类型可能是对于在一定天数内没有任何更新的项目,render_message 实现将返回"<project_name> hasn't been updated in x days"。实际上有几十种不同的场景。

标签: django django-models architecture


【解决方案1】:

如果您想使用模型来存储警报,Django proxy models 会很好地满足您的要求。

在高层次上,Django 具有三种继承:

  • Abstract Base Class:

    基类表未创建,所有继承模型获取基类的所有字段

  • Multi-table inheritance:

    Django 支持的第二种模型继承是当每个 层次结构中的模型本身就是一个模型。每个型号 对应自己的数据库表,可以查询和创建 分别。继承关系引入了 子模型及其每个父模型(通过自动创建的 OneToOneField)。

  • Proxy Models

    使用多表继承时,会创建一个新的数据库表 对于模型的每个子类。这通常是期望的行为, 因为子类需要一个地方来存储任何额外的数据字段 基类中不存在的。然而,有时你只 想要改变模型的 Python 行为——也许是改变 默认管理器,或添加新方法。

    这就是代理模型继承的用途:为 原始模型。您可以创建、删除和更新 代理模型,所有数据都将被保存,就像您使用 原始(非代理)模型。不同的是你可以改变 诸如默认模型排序或默认管理器之类的东西 代理,无需更改原件。

例子:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

class AlienPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        pass

很酷的是,即使对象存储在同一个表中,它也可以帮助您创建不同的管理界面 :) reference

【讨论】:

  • 谢谢,我不知道代理模型,它们看起来就像我想要的。问题是,我希望能够遍历特定用户的所有提醒列表,并能够为每个用户调用不同的 render_message 实现。但我认为我不能使用代理模型来做到这一点,因为我必须检索 Reminder 对象的集合,然后无法从那里获得不同的代理实现。我还应该提到,从分析的角度来看,了解所有提醒中每种类型的数量是有益的。
  • @user857159 讨论here 可能会让您感兴趣。基本上你可以创建父类的对象,过滤对象,然后调用方法。我还建议查看 cmets。希望这会有所帮助:)
【解决方案2】:

您可以将逻辑的自定义部分存储到数据库中。

class Reminder:

    message_template = forms.TextField()

    def render_message(self):
        render_message_dict = self.get_render_message_dict()
        return self.message_template.format(**render_message_dict)

    def get_render_message_dict(self):
        return {
            'project_name': self.project_name,
            'due_for_days': (date.today() - self.due_date).days,
            'updated_for_days': (date.today() - self.last_updated.date()).days,
        }

所以你设置:

reminder_a.message_template = '{project_name} is over due by {due_for_days} days'
reminder_b.message_template = '{project_name} hasn't been updated in {updated_for_days} days'

如果您有一组给定的消息模板,您也可以使用选择字段:

class Reminder:

    MESSAGE_TEMPLATE_CHOICES = [
        (1, _("{project_name} is over due by {due_for_days} days")),
        (2, _("{project_name} hasn't been updated in {updated_for_days} days")),
    ]

    message_template = forms.PositiveSmallIntegerField(
        choices=MESSAGE_TEMPLATE_CHOICES)

    def render_message(self):
        render_message_dict = self.get_render_message_dict()
        return self.message_template.format(**render_message_dict)

    def get_render_message_dict(self):
        return {
            'project_name': self.project_name,
            'due_for_days': (date.today() - self.due_date).days,
            'updated_for_days': (date.today() - self.last_updated.date()).days,
        }

这还可以将消息模板标记为可翻译。

【讨论】:

    猜你喜欢
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    • 2016-11-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-02-27
    • 1970-01-01
    相关资源
    最近更新 更多