【问题标题】:django post_signal triggered on form.save(commit=False)django post_signal 在 form.save(commit=False) 上触发
【发布时间】:2018-07-10 09:45:07
【问题描述】:

我有一个基础 User 模型和另外两个模型 CandidateCompany

User:
  email
  password
  is_company => default=False

表单类:

class CustomUserCreationForm(forms.Form):
    email = forms.EmailField(label='Enter email', widget=forms.EmailInput(attrs={'placeholder': 'Email Address', 'spellcheck':'False', 'autofocus':'True'}))
    password = forms.CharField(label='Enter password', min_length=8, widget=forms.PasswordInput(attrs={'placeholder': 'Password'}))

    def clean_email(self):
        email = self.cleaned_data['email'].lower()
        r = User.objects.filter(email=email)
        if r.count():
            raise  ValidationError("Email already exists")
        return email

    def clean_password(self):
        password = self.cleaned_data.get('password')
        return password

    def save(self, commit=True):
        user = User.objects.create_user(
            self.cleaned_data['email'],
            self.cleaned_data['password']
        )
        return user

我的用户管理器:

class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        try:
            validate_email(email)
            valid_email = True
        except ValidationError:
            valid_email = False
        if not valid_email:
            raise ValueError('Valid Email is required')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        subject = 'Activate Your Account'
        message = render_to_string('registration/account_activation_email.html', {
            'domain': 'example.in',
            'user': user,
            'uid': urlsafe_base64_encode(force_bytes(user.pk)),
            'token': account_activation_token.make_token(user),
        })
        user.email_user(subject, 'example <admin@example.in>', html_message=message)
        return user

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)

在我的用户创建视图中:

class SignUpView(View):
    is_company = False
    def get(self, request):
        if request.user.is_authenticated():
            return redirect(reverse_lazy('dashboard'))
        form = CustomUserCreationForm()
        return render(request, 'registration/signup.html', {'form': form, 'is_company':self.is_company})

    def post(self,request):
        form = CustomUserCreationForm(request.POST or None, request.FILES or None)
        if form.is_valid():
            user = form.save(commit=False)
            user.is_active = False
            user.is_company = self.is_company
            user.save()
            return render(request, 'registration/account_activation_sent.html')
        return render(request, 'registration/signup.html', {'form': form, 'is_company':self.is_company})

我为上述模型创建了一个 post_save 信号。

  @receiver(post_save, sender=User)
  def update_user_profile(sender, instance, created, **kwargs):
      if created:
          if instance.is_company:
              Company.objects.create(user=instance)
              instance.company.save()
        else:
            Candidate.objects.create(user=instance, 
                                   first_name=instance.is_company )
            instance.candidate.save()

urls.py:

url(r'^accounts/signup/company/',vw.SignUpView.as_view(is_company = True), name='signup_company')

问题是form.save(commit=False) 正在触发 post_save 信号。我最终将instance.is_company 设置为False,从而为公司创建了一个CandidateProfile。

但数据库用户表中的 is_company 填充为公司的 True。

请帮忙!

【问题讨论】:

  • 你能显示表单类吗?
  • @neverwalkaloner 是的。添加了我所拥有的一切。请检查。
  • 您已经覆盖了save 方法,因此它会忽略commit 参数的值,并且总是只进行创建。
  • 即使commit=False也会触发post_save信号吗?
  • 正如我所说,您的代码忽略提交参数。该信号由您拨打User.objects.create_user()触发。

标签: python django django-models django-forms django-views


【解决方案1】:

您应该在处理您的实例之前尝试断开 post_save 信号并稍后连接它。

@receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
    # disconnect post_save
    post_save.disconnect(update_user_profile, sender=sender)

    # do something with your instance

    # connect post_save
    post_save.connect(update_user_profile, sender=sender)

【讨论】:

    【解决方案2】:

    在您的表单中,您已覆盖 save 并始终调用 create_user,这将调用 user.save(using=self._db) 即不传递 commit 的值

    您可以做的是在您的 User 模型中覆盖 _create_user 以将 commit 的值传递给它

    class User(AbstractBaseUser):
        ...
        def _create_user(self, username, email, password, commit=True, **extra_fields):            
            if not username:
                raise ValueError('The given username must be set')
            email = self.normalize_email(email)
            username = self.model.normalize_username(username)
            user = self.model(username=username, email=email, **extra_fields)
            user.set_password(password)
            if commit:
                user.save(using=self._db)
            return user
    

    然后在你的表单中你可以将 commit 的值传递给它

    class CustomUserCreationForm(forms.Form):
        def save(self, commit=True):
            return User.objects.create_user(
                self.cleaned_data['email'],
                self.cleaned_data['password'],
                commit=False
            )
    

    编辑:再次查看这段代码,我觉得它很糟糕。 您最好在表单中修复您的逻辑以进行多次保存

    class CustomUserCreationForm(forms.Form):
        def save(self, commit=True):
            is_create = self.instance.pk is None
            user = super().save(commit=commit)
            user.set_password(self.cleaned_data['password'])
            user.save()
            if not is_create:
                return user
    
            if user.is_company:
                Company.objects.create(user=user)
                user.company.save()  # not really needed
            else:
                Candidate.objects.create(user=user, first_name=user.is_company)
                user.candidate.save()  # not really needed
            return user
    

    P.S:我个人不喜欢 django 信号,因为它们会导致对正在发生的事情感到困惑。最好在您的视图中或在表单中执行此操作保存。您应该只有一种创建新用户的方法,所以这应该不是什么大问题。

    【讨论】:

    • user.save(using=self._db, commit=commit) 未知争论提交。
    【解决方案3】:

    将您的参数更改为默认 False 并在保存之前检查这是否为真...如果您使用提交 false 或 true 调用它,您的方式并不重要,它们将始终保存在数据库中...因此它们是触发你的 post_save

    def save(self, commit=False):
        if commit:
            user = User.objects.create_user(
                self.cleaned_data['email'],
                self.cleaned_data['password']
            )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-02
      • 1970-01-01
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 2012-10-02
      • 1970-01-01
      相关资源
      最近更新 更多