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
CardByUserfor the givenCardand User exists, use its score. - If the
CardByUserdoesn'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
Cardobject'stagfield 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 CardByUsers with a score of 0.
So how could I get rid of those? Any ideas would be appreciated!