如何隐式(动态地)在模板中显示模型的属性

How to display model's attributes in template implicitly (dynamically)

我正在为我的游戏制作高分应用程序。 这是我的模型

class Skills(models.Model):
    attack = models.IntegerField(default=1)
    attack_xp = models.FloatField(default=0)

    constitution = models.IntegerField(default=10)
    constitution_xp = models.FloatField(default=0)

    mining = models.IntegerField(default=1)
    mining_xp = models.FloatField(default=0)

    strength = models.IntegerField(default=1)
    strength_xp = models.FloatField(default=0)

    agility = models.IntegerField(default=1)
    agility_xp = models.FloatField(default=0)

    smithing = models.IntegerField(default=1)
    smithing_xp = models.FloatField(default=0)

    defence = models.IntegerField(default=1)
    defence_xp = models.FloatField(default=0)

    herblore = models.IntegerField(default=1)
    herblore_xp = models.FloatField(default=0)

    fishing = models.IntegerField(default=1)
    fishing_xp = models.FloatField(default=0)

    ranged = models.IntegerField(default=1)
    ranged_xp = models.FloatField(default=0)

    thieving = models.IntegerField(default=1)
    thieving_xp = models.FloatField(default=0)

    cooking = models.IntegerField(default=1)
    cooking_xp = models.FloatField(default=0)

    prayer = models.IntegerField(default=1)
    prayer_xp = models.FloatField(default=0)

    crafting = models.IntegerField(default=1)
    crafting_xp = models.FloatField(default=0)

    firemaking = models.IntegerField(default=1)
    firemaking_xp = models.FloatField(default=0)

    magic = models.IntegerField(default=1)
    magic_xp = models.FloatField(default=0)

    fletching = models.IntegerField(default=1)
    fletching_xp = models.FloatField(default=0)

    woodcutting = models.IntegerField(default=1)
    woodcutting_xp = models.FloatField(default=0)

    runecrafting = models.IntegerField(default=1)
    runecrafting_xp = models.FloatField(default=0)

    slayer = models.IntegerField(default=1)
    slayer_xp = models.FloatField(default=0)

    farming = models.IntegerField(default=1)
    farming_xp = models.FloatField(default=0)

    construction = models.IntegerField(default=1)
    construction_xp = models.FloatField(default=0)

    hunter = models.IntegerField(default=1)
    hunter_xp = models.FloatField(default=0)

    summoning = models.IntegerField(default=1)
    summoning_xp = models.FloatField(default=0)

    dungeoneering = models.IntegerField(default=1)
    dungeoneering_xp = models.FloatField(default=0)

    overall_xp = models.FloatField(default=0)
    overall = models.IntegerField(default=0)
    user_name = models.CharField(max_length=30, primary_key=True)

如您所见,有很多属性。 共有25个技能等级和经验值。

我想按照模型 class 中指定的顺序在我的模板上显示它。 像这样

attack_level | attack_exp
constitutionlvl | constitution_exp
mining_level | mining_exp

在 table.

我知道我可以明确地做到这一点,但我不想那样做。


这是我目前尝试过的方法

我试过像这样执行原始查询

cursor.execute("SELECT * FROM highscores_skills WHERE user_name = %s", [user_name])

这样我就可以获取列表中的数据,然后在模板上循环它,但它按列名对数据进行排序。 攻击 -> 体质 -> 构造 -> 制作..等等。

如果我不使用星号而是使用列名,那么我得到了我想要的,但还有一个问题。

这是我的模板代码

{% for skill in skills %}
    <tr>
        <td class = "col1 align">
            <a href="">{{ skill }}</a>
        </td>
        <td class = "col2">
            <a href="">{{ player|lookup:forloop.counter0 }}</a>
        </td>
        <td class = "col3 align">
            <a href="">{{ player|lookup:forloop.counter }}</a>
        </td>
    </tr>
{% endfor %}

循环运行了 25 次。 skills 是一组技能名称。 我制作了一个自定义过滤器,以使用循环计数器按索引获取列表数据。 如果我最后设法将循环计数器的值 +1,这个东西就会起作用。

因为现在是这样的:

一个好的解决方案是创建两个模型字段 sub类,一个用于存储技能等级,一个用于存储技能 XP。这会给出类似的东西:

from django.db import models


class XpField(models.FloatField):
    '''
    Django model field used to store a skill XP.
    '''
    def __init__(self, *args, **kwargs):
        # Have a default "default" set to 0.
        if kwargs.get('default') is None:
            kwargs['default'] = 0

        super(XpField, self).__init__(*args, **kwargs)


class LevelField(models.IntegerField):
    '''
    Django model field used to store a skill level.
    Takes a required argument xp_field which is the XpField
    associated with this field's skill.
    '''
    def __init__(self, *args, **kwargs):
        # Have a default "default" set to 1.
        if kwargs.get('default') is None:
            kwargs['default'] = 1
        self.xp_field = kwargs.pop('xp_field')

        super(LevelField, self).__init__(*args, **kwargs)

如您所见,LevelField 存储了对 XpField 实例的引用,该实例应该是代表相同技能的 XP 的字段。

然后您可以使用这些字段 类:

来设置您的 Skills 模型
class Skills(models.Model):
    attack_xp = XpField()
    attack = LevelField(xp_field=attack_xp)

    # etc...

    def get_skills(self):
        '''
        Returns a list of dictionnaries containing name, level and XP
        for each of the model's skills.
        '''
        skill_values = []

        # Iterate over all the model's fields.
        for field in self._meta.get_fields():
            if isinstance(field, LevelField):
                level = getattr(self, field.name)
                xp = getattr(self, field.xp_field.name)
                skill_values.append({
                    'name': field.name,
                    'level': level,
                    'xp': xp,
                })

        return skill_values

get_skills 方法将遍历 Skills 模型字段,检测那些 LevelField 实例并使用它们构建技能数据列表。 Model._meta.get_fields 方法只出现在 Django 1.8 中。如果您使用的是旧版本,我认为 Model._meta.fields 可以解决问题。

然后您可以这样在模板中使用它:

{% for skill in skills.get_skills %}
    <tr>
        <td class = "col1 align">
            <a href="">{{ skill.name }}</a>
        </td>
        <td class = "col2">
            <a href="">{{ skill.level }}</a>
        </td>
        <td class = "col3 align">
            <a href="">{{ skill.xp }}</a>
        </td>
    </tr>
{% endfor %}

P.S:我没有测试代码,因此它可能包含错误,但我希望这能让您了解如何解决您的问题。