说我正在创造一个'#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吗?
答案 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。