在 Django 中指定一组相关字段时公认的最佳实践是什么?
What's the accepted best practice when specifying a set of related fields in Django?
假设我在 Django 中为 RPG 创建了一个 'Character' 模型,我希望所有属性(力量、敏捷、耐力等)都以类似的方式定义,并且能够以类似的方式处理。
显然我可以这样做:
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)
整数范围字段definition
但这并不是特别 DRY。这也意味着当我添加 ~30 种相似的技能时,我将变得更加干练,并且容易出错。
这些似乎不值得通过外键创建(这是解决方案 here),因为我需要为每个属性等指定 9 个外键。
是否已经有 DRY 的解决方案?还是我需要自己动手?
编辑:
我对属性和技能的主要用途是为技能检查提供总数,例如,如果我想知道要为 Programming
掷多少骰子,我可能会指定它使用字符 intelligence+computers
属性和技能,并且例如,从 'Tasks' 列表(例如属性和技能的其他组合)中显示该角色可以执行的前五项。
编辑:
看起来遵循 M2M 路线导致我过度规范化。有人能suggest improvements吗?
你为什么不和 ManyToManyField
一起工作?
这样您就可以轻松地添加和管理这些属性。也更易于管理,因为在您的情况下,您必须为每个属性添加一列。
对于需要重复字段的这种情况(如@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
到它。
这取决于您将如何使用这些统计数据。
例如,如果您需要根据玩家的智力获取玩家列表;有一个字段是 easier/better (您的解决方案)。该解决方案还涉及更少的代码(至少最初是这样)。
如果您需要列出统计数据,查看每个统计数据的最佳字符等,您最好使用您链接的解决方案。问问你自己,"intelligence" 应该是一个独立的 "entity"(带有 slug、描述等)吗?
这里还有一个选项;假设对于大多数查询,您只需要一个全局 "level" 值,并且统计信息的详细信息仅在检查玩家时有用:
-
from jsonfield.fields import JSONField
class User(…):
statistics = JSONField(help_text="Intelligence, Wits, etc.")
level = models.PositiveIntegerField(default=0)
这样您就可以将 30 多个统计数据打包到一个字段中,并且仍然可以快速查询用户 level/experience。 您还可以扩展 JSONField。
好吧,我会被怪死的,但还有 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"))
希望对您有所帮助。
编辑:您建议拆分重复的丑陋部分,列出其他文件中的所有字段,这样新的可能性,隐藏在抽象 class 中的巨大而不是枯燥的代码(或拆分 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)
这里有更多关于抽象 classes 的信息: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:如果我是你,我会选择快捷方式,如果你想拥有一些范围更广或更小的特殊技能,我会选择保留更多自定义的快捷方式,并且它对于任何可以接受的人来说更具可读性在你之后的项目。
假设我在 Django 中为 RPG 创建了一个 'Character' 模型,我希望所有属性(力量、敏捷、耐力等)都以类似的方式定义,并且能够以类似的方式处理。
显然我可以这样做:
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)
整数范围字段definition
但这并不是特别 DRY。这也意味着当我添加 ~30 种相似的技能时,我将变得更加干练,并且容易出错。
这些似乎不值得通过外键创建(这是解决方案 here),因为我需要为每个属性等指定 9 个外键。
是否已经有 DRY 的解决方案?还是我需要自己动手?
编辑:
我对属性和技能的主要用途是为技能检查提供总数,例如,如果我想知道要为 Programming
掷多少骰子,我可能会指定它使用字符 intelligence+computers
属性和技能,并且例如,从 'Tasks' 列表(例如属性和技能的其他组合)中显示该角色可以执行的前五项。
编辑:
看起来遵循 M2M 路线导致我过度规范化。有人能suggest improvements吗?
你为什么不和 ManyToManyField
一起工作?
这样您就可以轻松地添加和管理这些属性。也更易于管理,因为在您的情况下,您必须为每个属性添加一列。
对于需要重复字段的这种情况(如@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
到它。
这取决于您将如何使用这些统计数据。
例如,如果您需要根据玩家的智力获取玩家列表;有一个字段是 easier/better (您的解决方案)。该解决方案还涉及更少的代码(至少最初是这样)。
如果您需要列出统计数据,查看每个统计数据的最佳字符等,您最好使用您链接的解决方案。问问你自己,"intelligence" 应该是一个独立的 "entity"(带有 slug、描述等)吗?
这里还有一个选项;假设对于大多数查询,您只需要一个全局 "level" 值,并且统计信息的详细信息仅在检查玩家时有用:
-
from jsonfield.fields import JSONField
class User(…):
statistics = JSONField(help_text="Intelligence, Wits, etc.")
level = models.PositiveIntegerField(default=0)
这样您就可以将 30 多个统计数据打包到一个字段中,并且仍然可以快速查询用户 level/experience。 您还可以扩展 JSONField。
好吧,我会被怪死的,但还有 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"))
希望对您有所帮助。
编辑:您建议拆分重复的丑陋部分,列出其他文件中的所有字段,这样新的可能性,隐藏在抽象 class 中的巨大而不是枯燥的代码(或拆分 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)
这里有更多关于抽象 classes 的信息: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:如果我是你,我会选择快捷方式,如果你想拥有一些范围更广或更小的特殊技能,我会选择保留更多自定义的快捷方式,并且它对于任何可以接受的人来说更具可读性在你之后的项目。