gpt4 book ai didi

python - Django : Programmatically add Groups on User save

转载 作者:太空宇宙 更新时间:2023-11-04 06:40:30 25 4
gpt4 key购买 nike

保存用户后,我需要确保其实例默认与组相关联。

我找到了两种实现方式:

  • 覆盖模型的save() 方法

    模型.py:

    from django.contrib.auth.models import AbstractUser, Group


    class Person(AbstractUser):

    def save(self, *args, **kwargs):
    super().save(*args, **kwargs)
    to_add = Group.objects.get(id=1) # get_or_create is a better option
    instance.groups.add(to_add)
  • 捕获 post_save 信号:

    信号.py:

    from django.conf import settings
    from django.contrib.auth.models import Group
    from django.db.models.signals import post_save
    from django.dispatch import receiver


    @receiver(
    post_save,
    sender=settings.AUTH_USER_MODEL,
    )
    def save_the_group(instance, raw, **kwargs):
    if not raw:
    to_add = Group.objects.get(id=1) # get_or_create is a better option
    instance.groups.add(to_add)

这些方法在实现目标方面是否相同?

在 Django 中有更好的“良好实践”吗?

最佳答案

更新

为了更好地理解 Django 的工作原理,我发现困惑和解决方案都在 BaseModelForm.save() 中:

    ...
if commit:
# If committing, save the instance and the m2m data immediately.
self.instance.save()
self._save_m2m()
...

BaseModelForm._save_m2m() 中:

    ...
if f.name in cleaned_data:
f.save_form_data(self.instance, cleaned_data[f.name])
...

首先保存实例以获取主键(post_save发出信号),然后保存其所有多对多关系在 ModelForm.cleaned_data 上。

如果在 post_save 信号期间或在Model.save() 方法,它将被删除或覆盖BaseModelForm._save_m2m(),具体取决于ModelForm.cleaned_data

transaction.on_commit() - 在此作为解决方案进行讨论asnwer later and in a few other SO answers from which I was inspired from并被否决-将延迟信号的变化直到BaseModelForm._save_m2m() 已结束其操作。

虽然,在某些特殊情况下 transaction.on_commit() is very useful ,在这种情况下是一种矫枉过正,不仅因为它使情况复杂化一种尴尬的方式(最合适的信号是 m2m_changed as explained here )但是因为完全避免信号,而是 good .

因此,我会尽量给出一个兼顾这两种情况的解决方案:

  1. 如果实例是从Django Admin (ModelForm)中保存的
  2. 如果不使用 ModelForm 保存实例

模型.py

from django.contrib.auth.models import AbstractUser, Group


class Person(AbstractUser):
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if not getattr(self, 'from_modelform', False): # This flag is created in ModelForm
<add - remove groups logic>

表单.py

from django import forms
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import Group
from my_app.models import Person


class PersonChangeForm(UserChangeForm):
def clean(self):
cleaned_data = super().clean()
if self.errors:
return
group = cleaned_data['groups']
to_add = Group.objects.filter(id=1)
to_remove = Group.objects.filter(id=2)
cleaned_data['groups'] = group.union(to_add).difference(to_remove)
self.instance.from_modelform = True
return cleaned_data

class Meta:
model = Person
fields = '__all__'

这将适用于:

>>> p = Person()
>>> p.username = 'username'
>>> p.password = 'password'
>>> p.save()

或与:

from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import get_user_model
from django.forms.models import modelform_factory

user_creationform_data = {
'username': 'george',
'password1': '123!trettb',
'password2': '123!trettb',
'email': 'email@yo.gr',
}

user_model_form = modelform_factory(
get_user_model(),
form=UserCreationForm,
)
user_creation_form = user_model_form(data=user_creationform_data)
new_user = user_creation_form.save()

旧答案

基于 thisthat SO 问题以及标题为“How to add ManytoMany model inside a post_savesignal ”的文章我求助于解决方案,是使用 on_commit(func, using=None) :

The function you pass in will be called immediately after ahypothetical database write made where on_commit() is called would besuccessfully committed.

from django.conf import settings
from django.contrib.auth.models import Group
from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver


def on_transaction_commit(func):
''' Create the decorator '''
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))

return inner


@receiver(
post_save,
sender=settings.AUTH_USER_MODEL,
)
@on_transaction_commit
def group_delegation(instance, raw, **kwargs):
to_add = Group.objects.get(id=1)
instance.groups.add(to_add)

上面的代码没有考虑到 every login causes apost_save signal

深入挖掘

相关 Django ticket 中的一个关键点是如果 save() 调用是在原子事务以及依赖于group_delegation() 函数的结果。

@transaction.atomic
def accept_group_invite(request, group_id):
validate_and_add_to_group(request.user, group_id)
# The below line would always fail in your case because the

提交时# 在退出这个函数之前不会调用接收器。if request.user.has_perm('group_permission'):做一点事()...

Django docs 更详细地描述了约束条件on_commit() 成功运行。

测试

During testing ,使用 TransactionTestCase 或 使用 pytest 进行测试时的 @pytest.mark.django_db(transaction=True) 装饰器。

This 是我如何测试此信号的示例。

关于python - Django : Programmatically add Groups on User save,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53603057/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com