Reputation: 447
I'm making a highscore app for my game. This is my model
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)
as you can see there are a lot of attributes. There are a total of 25 skill levels and experience points.
I want to display this on my template in the order specified in model class. Like this
attack_level | attack_exp
constitutionlvl | constitution_exp
mining_level | mining_exp
in a table.
I know I could do it explicitly but I don't want to do that.
I tried executing a raw query like this
cursor.execute("SELECT * FROM highscores_skills WHERE user_name = %s", [user_name])
so that I can get the data in a list and then loop it on template but it orders the data by column name. attack -> constitution -> construction -> crafting..etc like that.
If I do not use asterisk and use column names instead then I get what I want but there's one more problem.
This is my template code
{% 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 %}
The loop runs 25 times. skills is an array of skill names. I made a custom filter to get list data by index using loop counter. This thing will work if I manage to +1 the value of loop counter in the end.
Because this is how it looks right now:
Upvotes: 1
Views: 216
Reputation: 5554
A good solution would be to create two Model field subclasses, one for storing skill level and one for storing skill XP. Which would give something like:
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)
As you can see the LevelField
stores a reference to a XpField
instance which should be the field representing XP for the same skill.
You can then setup your Skills
model by using those field classes:
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
The get_skills
method will iterate over the Skills
model fields, detect the ones that are LevelField
instances and use them to build a list of skills' data.
The Model._meta.get_fields method only appeared in Django 1.8. If your are using an older version, I think Model._meta.fields
will do the trick.
You can then use it in your template this way:
{% 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: I did not test the code so it may contain errors but I hope this gives you an idea on how to solve your issue.
Upvotes: 1