Изменение значений экземпляра после изменения захвата с помощью Django Signals
У меня есть модель Course
, которая имеет отношение ManyToMany с моей моделью CustomUser
:
class CustomUser(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('Email Address'), unique=True)
user_name = models.CharField(_('User Name'), max_length=150, unique=True)
# and a lot of other fields and stuff
class Course(models.Model):
enrolled_users = models.ManyToManyField(CustomUser, related_name="enrolls", blank=True)
previous_enrolled_users = models.ManyToManyField(CustomUser, related_name="previous_enrolls", blank=True)
course_name = models.CharField(_("Course Name"), max_length=200)
Я пытаюсь реализовать следующее: когда пользователь заканчивает курс (и таким образом пользователь удаляется из enrolled_users
), мое приложение сохраняет этого пользователя в previous_enrolled_users
, чтобы я мог узнать пользователей, которые ранее были записаны на этот курс.
Я реализовал прослушивание сигнала m2m_changed
следующим образом:
def listen_m2mchange(sender, instance, model, pk_set, action, **kwargs):
if action == 'pre_remove':
# I'm trying to guess what to do
m2m_changed.connect(listen_m2mchange, sender=Course.enrolled_users.through)
Таким образом, всякий раз, когда я удаляю пользователя из курса, Django сигнализирует m2m_changed
, и я перехватываю этот сигнал. Я знаю, что instance
является экземпляром класса Course
и что model
является экземпляром того класса CustomUser
, который я удаляю. Я не могу понять, как, используя экземпляр класса Course
, я могу добавить CustomUser
в previous_enrolled_users. Любая помощь будет очень признательна.
вот models.py, который я использовал для воссоздания проблемы:
from django.contrib.auth.base_user import BaseUserManager
from django.db import models
from django.contrib.auth.models import AbstractUser
from django.utils.translation import ugettext_lazy as _
from django.db.models.signals import m2m_changed
class CustomUserManager(BaseUserManager):
"""
Custom user model manager where username is unique identifiers
able to add more fields to Django basic User model.
"""
def create_user(self, username, password, **extra_fields):
"""
Create and save a User with the given email and password.
"""
if not username:
raise ValueError(_('The username must be set'))
user = self.model(username=username, **extra_fields)
user.set_password(password)
user.save()
return user
def create_superuser(self, username, password, **extra_fields):
"""
Create and save a SuperUser with the given email and password.
"""
extra_fields.setdefault('is_staff', True)
extra_fields.setdefault('is_superuser', True)
extra_fields.setdefault('is_active', True)
if extra_fields.get('is_staff') is not True:
raise ValueError(_('Superuser must have is_staff=True.'))
if extra_fields.get('is_superuser') is not True:
raise ValueError(_('Superuser must have is_superuser=True.'))
return self.create_user(username, password, **extra_fields)
def create_staffuser(self, username, first_name, last_name, password=None):
if not username:
raise ValueError("User must have an email")
if not password:
raise ValueError("User must have a password")
if not first_name:
raise ValueError("User must have a first name")
if not last_name:
raise ValueError("User must have a last name")
user = self.model(
email=self.normalize_email(username)
)
user.first_name = first_name
user.last_name = last_name
user.set_password(password) # change password to hash
user.is_admin = False
user.is_staff = True
user.save(using=self._db)
return user
class CustomUser(AbstractUser):
email = models.EmailField(_('Email Address'), unique=True)
user_name = models.CharField(_('User Name'), max_length=150, unique=True)
# and a lot of other fields and stuff
objects = CustomUserManager()
class Course(models.Model):
enrolled_users = models.ManyToManyField(
CustomUser, blank=True)
previous_enrolled_users = models.ManyToManyField(
CustomUser, related_name="previous_enrolls", blank=True)
course_name = models.CharField(_("Course Name"), max_length=200)
def listen_m2mchange(sender, **kwargs):
pass
m2m_changed.connect(listen_m2mchange, sender=Course.enrolled_users.through)
полезный код в оболочке Django:
In [1]: from hello.models import CustomUser, Course
In [2]: U3 = CustomUser.objects.create(username='test3', user_name='tes
...: t3', email='test3@g.com')
In [3]: U3 = CustomUser.objects.create(username='test4', user_name='tes
...: t4', email='test4@g.com')
In [4]: C = Course.objects.create(course_name='some course3')
In [5]: C.enrolled_users.add(U3)
In [6]: C.enrolled_users.get()
Out[6]: <CustomUser: test4>
In [7]: C.enrolled_users.remove(U3) # removes the user from the set
Надеюсь, это было полезно.
Попробуйте это:
def listen_enrolled_users_m2mchange(sender, instance, model, pk_set, action, **kwargs):
if action == 'post_remove':
instance.previous_enrolled_users.add(*pk_set)
m2m_changed.connect(listen_enrolled_users_m2mchange, sender=Course.enrolled_users.through)
pk_set
здесь будет набор первичных ключей, которые участвовали в изменении поля enrolled_users
в Course
. То есть, когда действие будет выполнено post_remove
, все удаленные CustomUser
первичные ключи будут переданы в pk_set
kwarg.
Это означает, что когда поступает сигнал об изменениях на enrolled_users
из Course
, мы можем проверить, является ли это действие удалением. В этом случае, те же самые pk_set
, которые мы получили и которые удалены из enrolled_users
, могут быть непосредственно добавлены в previous_enrolled_users
.