2009-06-05 4 views
3

Я разрабатываю программу обучения лексике с Django (немецкий-шведский).Модели Django: как вернуть значение по умолчанию в случае несуществующего отношения внешнего ключа?

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

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

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

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

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

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

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

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

Может быть, это можно сказать Django следующее при расчете среднего балла за Card:

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

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

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

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.

Сейчас, на мой взгляд код, мне нужно создать QuerySet из 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 s со счетом 0.

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

ответ

2

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

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 
+0

Если это только для части геттера, я бы использовал свойство() в качестве декоратора. –

1

Это не может быть полностью связана с вашим вопросом, но это выглядит как 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) 
... 
+0

Спасибо! Выглядит многообещающе. Один вопрос, однако: если есть новый ManyToManyField на Карте - CardByUser все еще нуждается в ForeignKey для пользователя (или вы забыли удалить его в свой код)? Если это действительно необходимо, я хотел бы знать, почему. – m3mitsuppe

+1

Это действительно необходимо, когда вы «вручную» определяете отношения «многие ко многим», используя аргумент 'through'. См. Http://docs.djangoproject.com/ru/dev/topics/db/models/#extra-fields-on-many-to-many-relationships: «Когда вы настраиваете промежуточную модель, вы явно указываете внешние ключи к моделям, которые участвуют в отношении ManyToMany. Это явное объявление определяет, как связаны две модели ». – Arnaud

+0

Хорошо, правильно. Но я не уверен, что я не должен явно создавать объекты CardByUser, как вы пишете в своем ответе. Напротив, документы говорят, что такие отношения ManyToMany создаются именно путем создания экземпляров промежуточной модели. Именно этого я и хотел бы избежать. – m3mitsuppe

 Смежные вопросы

  • Нет связанных вопросов^_^