Модели Django: как вернуть значение по умолчанию в случае несуществующего отношения внешнего ключа? - PullRequest
3 голосов
/ 05 июня 2009

Я разрабатываю программу обучения словарного запаса с Джанго (немецкий-шведский).

Словарные данные приложения состоят из большого количества "словарных карточек", каждая из которых содержит одно или несколько немецких слов или терминов, соответствующих одному или нескольким шведским терминам.

Обучение доступно только для зарегистрированных пользователей, поскольку приложение отслеживает производительность пользователя, сохраняя score для каждой словарной карты.

Словарные карточки имеют уровень (базовый, продвинутый, экспертный) и любое количество тегов, присвоенных им.

Когда зарегистрированный пользователь начинает обучение, приложение должно рассчитать средние оценки пользователя для каждого из уровней и тегов, чтобы он мог сделать свой выбор.

Я решил эту проблему, введя модель с именем CardByUser, которая имеет score и поле и ForeignKey отношения с моделями User и Card. Теперь я могу использовать функцию агрегирования Джанго для вычисления средних баллов.

Большой недостаток: это работает, только если есть экземпляр CardByUser для каждого экземпляра Карты, который в настоящее время существует в БД, даже если пользователь обучил только 100 карт. Мое текущее решение заключается в создании всех этих CardByUser экземпляров при создании Card и при регистрации пользователя. Это, конечно, довольно неэффективно как с точки зрения памяти базы данных, так и с точки зрения вычислительного времени (регистрация пользователя занимает довольно много времени).

И это выглядит довольно не элегантно, что меня больше всего беспокоит.

Есть ли лучший способ сделать это?

Возможно, можно подсказать Джанго следующее при расчете среднего балла для Card:

  • Если CardByUser для данного Card и пользователя существует, используйте его счет.
  • Если CardByUser не существует, используйте значение по умолчанию -> оценка 0.

Можно ли это сделать? Если да, то как?

Редактировать: Уточнение Спасибо S.Lott's за первый ответ, но я думаю, что моя проблема немного сложнее. Плохо, я пытаюсь уточнить, используя какой-то реальный код из моих моделей.

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 ...

Это означает, что многие CardByUser объекты связаны с одним Card.

Теперь в моем коде представления мне нужно создать набор запросов из CardByUser объектов, которые удовлетворяют следующим критериям:

  • поле tag связанного Card объекта содержит определенную строку (я сейчас тоже не оптимален, но не в фокусе моего вопроса ...)
  • пользователь является текущим пользователем

Тогда я могу агрегировать по баллам. Мой текущий код выглядит так (сокращенно):

user_cards = CardByUser.objects.filter(user=current_user)
                               .filter(card__tags__contains=tag.name)
avg = user_cards_agg.aggregate(Avg('score'))['score__avg']

Если CardByUser для текущего пользователя и Card не существует, оно просто не будет включено в агрегацию. Вот почему я создаю все эти CardByUser с счетом 0.

Так, как я мог избавиться от них? Любые идеи будут оценены!

Ответы [ 2 ]

2 голосов
/ 05 июня 2009

Для этого предназначены методы (и, возможно, свойства).

class OptionalFKWithDefault( models.Model ):
    another = models.ForeignKey( AnotherModel, blank=True, null=True )
    @property
    def another_score( self ):
        if self.another is None:
            return 0
        else:
            return self.another.score
1 голос
/ 08 июня 2009

Возможно, это не совсем связано с вашим вопросом, но похоже, что CardByUser действительно должно быть отношением «многие ко многим» с дополнительным полем. (см. http://docs.djangoproject.com/en/dev/topics/db/models/#extra-fields-on-many-to-many-relationships)

Может быть, вы могли бы изменить свою модель таким образом?

class Card(models.Model):
    entry_sv = models.CharField(max_length=200)
    entry_de = models.CharField(max_length=200)
    ... more fields ...
    users = models.ManyToManyField(User, through='CardByUser')

class CardByUser(models.Model):
    user = models.ForeignKey(User)
    card = models.ForeignKey(Card)
    score = models.IntegerField(default=0)

Тогда вам не нужно будет явно создавать объекты CardByUser, так как об этом позаботится Django. Вы также сможете упростить свой запрос агрегации:

user_cards = Card.objects.filter(users=current_user)
                         .filter(tags__contains=tag.name)
...
...