I am developing a vocabulary training program with Django (German-Swedish).
The app's vocabulary data consists of a large number of "vocabulary cards", each of which contains one or more German words or terms that correspond to one or more Swedish terms.
Training is only available for registered users, because the app keeps track of the user's performance by saving a score
for each vocabulary card.
Vocabulary cards have a level (basic, advanced, expert) and any number of tags assigned to them.
When a registered user starts a training, the application needs to calculate the user's average scores for each of the levels and tags, so he can make his selection.
I have solved this problem by introducing a model named CardByUser
that has a score
and field and ForeignKey
relationships to the models User
and Card
. Now I can use Django's aggregation function calculate the average scores.
The big disadvantage: this works only if there is a CardByUser
instance for each and every Card instance that currently exists in the DB, even if the user has only trained 100 cards. My current solution is to create all those CardByUser
instances on Card
creation and when a user is registered. This is, of course, rather ineficient both in terms of data base memory and of computing time (registering a user takes quite a while).
And it seems quite inelegant, which kind of bugs me the most.
Is there a better way to do this?
Maybe it is possible to tell Django the following when calculating the average score for a Card
:
- If a
CardByUser
for the givenCard
and User exists, use its score. - If the
CardByUser
doesn't exist, use a default value --> the score 0.
Can this be done? If so, how?
Edit: Clarification Thanks S.Lott's for the first answer, but I think that my problem is a bit more complicated. My bad, I'm trying to clarify using some actual code from my models.
class Card(models.Model):
entry_sv = models.CharField(max_length=200)
entry_de = models.CharField(max_length=200)
... more fields ...
class CardByUser(models.Model):
user = models.ForeignKey(User)
card = models.ForeignKey(Card, related_name="user_cards")
score = models.IntegerField(default=0)
... more fields ...
This means many CardByUser
objects are related to a single Card
.
Now in my view code, I need to create a queryset of CardByUser
objects that fulfill the following criteria:
- the related
Card
object'stag
field contains a certain string (I now that's not optimal either, but not the focus of my question...) - the user is the current user
Then I can aggregate over the scores. My current code looks like this (shortened) :
user_cards = CardByUser.objects.filter(user=current_user)
.filter(card__tags__contains=tag.name)
avg = user_cards_agg.aggregate(Avg('score'))['score__avg']
If a CardByUser
for the current user and Card
does not exist, it will simply not be included in the aggregation. That's why I create all those CardByUser
s with a score of 0.
So how could I get rid of those? Any ideas would be appreciated!