Как реализовать автоматическое создание записей по полю ManyToManyField:
Я хочу создать атчивки для юзера, для понимания вот сами таблички:
class User(BaseUser, models.Model):
objects = BaseUserManager()
is_organization = models.BooleanField(default=False)
class UserAchievement(models.Model):
name = models.CharField(max_length=128, unique=True)
description = models.CharField(max_length=256)
final_value = models.SmallIntegerField()
progress_right_now = models.FloatField(default=0)
given_exp = models.SmallIntegerField()
#image = models.ImageField()
class UsersAchievements(models.Model):
user = models.ManyToManyField(to=User)
achievement = models.ManyToManyField(to=UserAchievement)
is_achieved = models.BooleanField(default=False)
Сделал промежуточную табличку и для будущей логики нужное поле, чтобы позже проверять выполнена атчивка или нет, но вопрос в том, как бы правильней при создании юзера создавать ему записи в таблице UsersAchievements по всем существующим атчивкам, делать это сигналом пост_сев? или переопределить метод save модели юзер? Пытался реализовать 2 вариант, но сталкивался с ошибками
Direct assignment to the forward side of a many-to-many set is prohibited. Use user.set() instead. User' object has no attribute 'userachievements_set'
Потому хочу спросить как лучше правильнее и как именно это реализовать?
сейчас пробую сделать сигнал:
@receiver(post_save, sender=User)
def create_users_achievements(sender, instance, created, **kwargs):
if created and not instance.usersachievements_set.exists():
achievements = UserAchievement.objects.all()
instance.usersachievements_set.set(achievements)
но возникает следующая ошибка -
TypeError at /admin/custom_user/user/add/
Field 'id' expected a number but got <UserAchievement: название - Тест, опыт - 20>.
UPD Обновил модели
class UserAchievement(models.Model):
name = models.CharField(max_length=128, unique=True)
description = models.CharField(max_length=256)
final_value = models.SmallIntegerField()
progress_right_now = models.FloatField(default=0)
given_exp = models.SmallIntegerField()
image = models.ImageField(upload_to='users/achievements', default='static/achievement/achiv.jpg')
class User(BaseUser, models.Model):
objects = BaseUserManager()
picture_profile = models.ImageField(upload_to='users/users_picture_profile', null=True, blank=True, default='static/picture_profile/pp.jpg')
is_organization = models.BooleanField(default=False)
achievements = models.ManyToManyField(to=UserAchievement, blank=True)
class UserAchievesStatus(models.Model):
user = models.ForeignKey(to=User, on_delete=models.CASCADE)
achievement = models.ForeignKey(to=UserAchievement, on_delete=models.CASCADE)
is_achieved = models.BooleanField(default=False)
теперь пишу сигнал -
@receiver(post_save, sender=User)
def create_user_achievements(sender, instance, created, **kwargs):
print('signal working')
if created:
print(instance.email)
achievements = UserAchievement.objects.all()
print(achievements)
instance.achievements.add(*achievements)
print(instance.achievements)
сигнал срабатывает но с ошибкой -
signal working
[email protected]
<QuerySet [<UserAchievement: название - Тест, опыт - 10>, <UserAchievement: название - Тест2, опыт - 2>]>
custom_user.UserAchievement.None
и вот проверяю еще раз в консоли
user = User.objects.last()
user
<User: [email protected]>
user.achievements.all()
<QuerySet []>
Когда делаю вручную через админку
все работает корректно
user = User.objects.last()
user
<User: [email protected]>
user.achievements.all()
<QuerySet [<UserAchievement: название - Тест, опыт - 10>, <UserAchievement: название - Тест2, опыт - 2>]>
Ответы (2 шт):
Третья таблица лишняя. Ее Django сам создаст. Просто в поле user добавь поле m2m на модель ачивок
Для того, чтобы автоматически создавать обьекты achievements для созданого юзера через связь many2many, я решил использовать сигналы.
Например:
@receiver(post_save, sender=User)
def create_user_achievements(sender, instance, created, **kwargs):
if created:
all_achievements = UserAchievement.objects.all()
for achievement in all_achievements:
user_achievement_status = UserAchievementStatus.objects.create(
user=instance,
achievement=achievement,
)
instance.achievements.set([achievement])
Более подробно в документации - Many-to-many relationships