【问题标题】:Updating the a user profile upon user save在用户保存时更新用户配置文件
【发布时间】:2017-05-19 16:41:57
【问题描述】:

我正在使用'User profile' 方法来扩展我的用户模型,如下所示:

# models.py
class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, primary_key=True)
    my_field = models.CharField(max_length=100)

# signals.py
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

使用这种方法,我必须显式调用user.profile.save(),这对我来说感觉很笨拙,因为我希望profile 给人一种错觉,它是User 对象的一部分:

# views.py
def some_func(request):
    user = User.objects.create_user('dummy', 'dummy@dummy.com', '12345678')
    user.profile.my_field = 'hello'
    user.save()          # This does not persist the profile object...
    user.profile.save()  # ...this does

为了解决这个问题,我将create_user_profile() 更改为以下内容,这样可以:

# signals.py
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
   profile = UserProfile.objects.get_or_create(user=instance)
   profile.save()

我遇到的许多示例都没有使用这种方法。使用这种方法有什么注意事项吗?

【问题讨论】:

    标签: django django-models django-signals


    【解决方案1】:

    更好的方法是指定自定义用户模型。

    from django.contrib.auth.models import AbstractUser
    from django.db import models
    
    class User(AbstractUser):
        custom_field = models.ForeignKey(
            'contracts.Contract'
        )
        ...
    
        class Meta(AbstractUser.Meta):
            swappable = 'AUTH_USER_MODEL'
    

    您必须更新定义AUTH_USER_MODEL 属性的settings.py

    AUTH_USER_MODEL = 'app_name.User'
    

    【讨论】:

    • 不错。为什么我需要custom_field 作为外键?我不能在自定义 User 对象上定义一些字符字段吗?
    • 是的,你可以。这只是示例字段。
    【解决方案2】:

    您可以像这样使用自定义用户模型:

    class Profile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE)
    
        def __str__(self):
            return f'{self.user.username} Profile'
    

    然后是 signals.py 文件:

    from django.db.models.signals import post_save
    from django.contrib.auth.models import User
    from django.dispatch import receiver
    from .models import Profile
    
    @receiver(post_save, sender=User)
    def create_profile(sender, instance, created, **kwargs):
        if created:
            Profile.objects.create(user=instance)
    
    
    @receiver(post_save, sender=User)
    def save_profile(sender, instance, **kwargs):
        instance.profile.save()
    

    signals.py 文件在这里的作用是在创建 User 类型的新 Profile 对象时通知您,您可以进一步使用该对象来创建更新/创建用户配置文件的表单。

    为了使所有这些工作,您需要在应用程序的 apps.py 文件中导入 signals.py 文件。例如,这就是您的 apps.py 文件的样子:

    from django.apps import AppConfig
    
    
    class UsersConfig(AppConfig):
        name = 'users'
    
        def ready(self):
            import users.signals
    

    【讨论】:

      【解决方案3】:

      是的,有几个。在以下情况下不会触发 post_save 信号。

      1如果save方法没有成功保存对象(比如发生IntegrityError时)

      2 当您拨打MyModel.objects.update()

      3 当你重写保存方法而忘记调用超类方法时。

      4 当您的信号接收器未成功注册时。

      在这些情况下,您的个人资料将不会被保存。

      【讨论】:

      • 除了第 2 项之外,第一种方法也有同样的注意事项。我说的对吗?
      • 那么您是在显式调用 save 而不是依赖于幕后活动。所以这些警告都不重要
      • 但我并不是说 post_save 是一个坏方法,它真的很好。我的回答是因为你问了!!
      猜你喜欢
      • 1970-01-01
      • 2012-08-01
      • 2022-01-03
      • 1970-01-01
      • 2022-01-07
      • 1970-01-01
      • 1970-01-01
      • 2018-09-11
      • 1970-01-01
      相关资源
      最近更新 更多