在Django中指定一组相关字段时,接受的最佳做法是什么?

时间:2015-01-27 11:50:18

标签: python django django-models

说我正在创造一个'#39;字符'在Django中为RPG建模,我想要以类似的方式定义所有属性(力量,敏捷,耐力等),并且能够以类似的方式对待。

显然我可以这样做:

intelligence = IntegerRangeField(min_value=1, max_value=10)
wits= IntegerRangeField(min_value=1, max_value=10)
resolve = IntegerRangeField(min_value=1, max_value=10)
strength = IntegerRangeField(min_value=1, max_value=10)
dexterity = IntegerRangeField(min_value=1, max_value=10)
stamina = IntegerRangeField(min_value=1, max_value=10)
presence = IntegerRangeField(min_value=1, max_value=10)
manipulation = IntegerRangeField(min_value=1, max_value=10)
composure = IntegerRangeField(min_value=1, max_value=10)

IntegerRangeField definition

但那不是特别DRY。这也意味着当我增加约30种相似的技能时,我会变得更加干燥,并且容易出错。

这些似乎不值得通过外键创建(这是解决方案here),因为我需要为每个属性等指定其中的9个。

这已经是DRY的解决方案吗?或者我需要自己动手?

编辑:
我对属性和技能的主要用途是提供技能检查的总数,例如,如果我想知道要为Programming滚动多少骰子,我可能会指定它使用字符intelligence+computers属性和技能,以及例如,来自“任务”的列表。 (例如,属性和技能的其他组合)显示角色可以执行的前五名。


编辑:

看起来跟随M2M路线导致我过度正常化。有人能够suggest improvements吗?

4 个答案:

答案 0 :(得分:4)

您可以在这种情况下使用多对多的字段关系(例如@RonaldOldenburger建议),您需要重复字段。例如:

class Skill(models.Model):
    name = models.CharField(max_length=255)
    value = IntegerRangeField(min_value=1, max_value=10)

class Character(models.Model):
    name = models.CharField(max_length=255)
    skill = models.ManyToManyField(Skill)

如果您使用modelform创建Character模型实例,则可以使用model formset factory向其添加Skill

答案 1 :(得分:2)

为什么不使用ManyToManyField

通过这种方式,您可以轻松添加和管理这些属性。也更容易管理,因为在您的情况下,您必须为每个属性添加一列。

答案 2 :(得分:2)

好的,我会被归咎于死亡,但也有MonkeyPatch技术(我喜欢并使用它但可能导致可读性差)或者你可以做一个简单的快捷方式。

这里是猴子补丁,最好的部分是,如果你有某个技能指数,你可以使用它:

SKILLS = ["skill_1", "skill_2", "skill_3"]
for skill in SKILLS:
    IntegerRangeField(
        min_value=min_value, max_value=max_value
    ).contribute_to_class(MyModel, skill)

注意:当您将其保存在正确的应用模型文件中时,它适用于迁移

这里有捷径:

def new_skill(min_value=1, max_value=10, **kwargs):
    return IntegerRangeField(
        min_value=min_value, max_value=max_value, **kwargs)

class MyModel(models.Model):
    skill_1 = new_skill()
    skill_2 = new_skill()
    skill_3 = new_skill(verbose_name=_("translatable skill name"))

希望它有所帮助。

编辑:你建议拆分重复的丑陋部分,列出另一个文件中的所有字段,这样新的可能性,在抽象类中隐藏丑陋而不是干代码(或者拆分models.py,但它更难解释)。

class RpgSkills(models.Model):
    skill_1 = new_skill()
    skill_2 = new_skill()
    skill_3 = new_skill(verbose_name=_("translatable skill name"))

    class Meta:
        abstract = True # means will not have any data table by it's own

然后在你的models.py中:

from skills_utils import RpgSkills

class Character(RpgSkills):
    name = CharField(max_length=128)

这里有关于抽象类的更多信息:https://docs.djangoproject.com/en/1.7/topics/db/models/#abstract-base-classes

如果你是一个有趣的人,你可以把所有人结合在一起:

这里是工具:

def skill_fields(*skills):
    class MyAbstractModel(models.Model):
        class Meta:
            abstract = True

    for skill in skills:
        new_skill().contribute_to_class(MyAbstractModel, skill)
    return MyAbstractModel

这里的用法:

from skills_utils import skill_fields

class Character(skill_fields("skill_1", "skill_2", *settings.MY_APP_SKILL_LIST)):
    name = CharField(max_length=128)

但最后一个是那些只有最好的幽默的人; - )

PS:如果我是你,我会选择快捷方式,如果你想拥有更广泛或更小范围的特殊技能,可以开放更多的自定义,并且它更容易阅读以后谁可以参加项目你。

答案 3 :(得分:1)

这取决于您将如何使用这些统计数据。

  • 如果您需要根据他们的情报获取球员名单,例如;拥有一个领域更容易/更好(你的解决方案)。该解决方案还涉及更少的代码(至少在最初阶段)。

  • 如果您需要列出统计信息,请查看每个统计信息的最佳字符等,最好使用您链接的解决方案。问问自己,应该"情报"成为一个"实体"独自(用slu ,,描述等)?

  • 这是另一种选择;让我们说你只需要一个全球性的水平"大多数查询的值和统计信息的详细信息仅在检查播放器时有用:

-

from jsonfield.fields import JSONField


class User(…):
    statistics = JSONField(help_text="Intelligence, Wits, etc.")
    level = models.PositiveIntegerField(default=0)

通过这种方式,您可以在一个字段中打包30多个统计信息,并且仍可以快速查询用户级别/体验。 您还可以扩展JSONField。