Django простая аннотация для двух моделей - PullRequest
0 голосов
/ 07 ноября 2019

У меня есть три модели: User, Achievement и UserAchievement:

class User(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)
    ...

class Achievement(models.Model):
    name = models.CharField(max_length=64)
    points = models.PositiveIntegerField(default=1)

class UserAchievement(models.Model):
    # Use a lazy reference to avoid circular import issues
    user = models.ForeignKey('users.User', on_delete=models.CASCADE)
    achievement = models.ForeignKey(Achievement, on_delete=models.CASCADE)

Я хочу аннотировать пользователей с помощью столбца «Баллы», в котором суммируется общее количество баллов, заработанных завсе их достижения, как указано в таблице UserAchievement.

Но, очевидно, я не совсем в курсе того, как работают аннотации. Когда я пытаюсь:

users = User.objects.annotate(points=Sum('userachievement__achievement__points'))
for u in users:
   print(u.email, u.points)

Вылетает с:

Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Users/dylan/.local/share/virtualenvs/server-E23dvZwD/lib/python3.7/site-packages/django/db/models/query.py", line 274, in __iter__
self._fetch_all()
  File "/Users/dylan/.local/share/virtualenvs/server-E23dvZwD/lib/python3.7/site-packages/django/db/models/query.py", line 1242, in _fetch_all
self._result_cache = list(self._iterable_class(self))
  File "/Users/dylan/.local/share/virtualenvs/server-E23dvZwD/lib/python3.7/site-packages/django/db/models/query.py", line 78, in __iter__
setattr(obj, attr_name, row[col_pos])
AttributeError: can't set attribute

1 Ответ

1 голос
/ 07 ноября 2019

Вы можете использовать ManyToManyField от User до Achievement, используя UserAchievement в качестве сквозной таблицы для упрощения ваших запросов

class User(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)
    achievements = models.ManyToManyField('Achievement', through='UserAchievement')
    ...

Это можно использовать так же, как и дляаннотации

User.objects.annotate(points=Sum('achievements__points'))

И если у вас есть экземпляр пользователя

user.achievements.all()

Что касается ошибки, я убежден, что у вас есть свойство в вашей модели пользователя с именем points

...