Django - 在创建用户时创建用户个人资料

26

我正在遵循Django文档这里的指导,以实现一个简单的目标:一旦创建了新用户就创建用户资料。

我有一个'accounts'应用程序,我的accounts.models看起来像这样:

# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from main.models import Store

class UserProfile(models.Model):

    GENRE_CHOICES = (
        ('m', 'Masculino'),
        ('f', 'Feminino'),
    )
    MARITAL_STATUS_CHOICES = (
        ('s', 'Solteiro'),
        ('c', 'Casado'),
        ('d', 'Divorciado'),
        ('v', 'Viúvo'),
    )

    user = models.ForeignKey(User, unique=True)
    birth_date = models.DateField()
    genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
    address = models.CharField(max_length=150)
    postal_code_4 = models.PositiveIntegerField()
    postal_code_3 = models.PositiveIntegerField()
    locatity = models.CharField(max_length=30)
    marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES)
    child_amount = models.PositiveSmallIntegerField()
    is_merchant = models.BooleanField(default=False)
    store = models.ForeignKey(Store, null=True)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)

post_save.connect(create_user_profile, sender=User)

我觉得一切看起来都很好,但是当我尝试添加新用户(使用django管理界面)时,我没有得到新创建的用户和用户配置文件,而是收到以下错误信息: InternalError at /admin/auth/user/add/ current transaction is aborted, commands ignored until end of transaction block

这里是跟踪错误的部分:

/djangoProjects/lwboanova/lwboanova/apps/accounts/models.py in create_user_profile

34: UserProfile.objects.create(user=instance)

看起来像是完整性错误,但我不知道原因。

如果你们中的任何人能够帮助我,那就太好了。


3
你是否安装了Django工具栏?如果是,请尝试禁用它以获得更好的错误信息。 - Ashray Baruah
我很惊讶地看到一些答案在UserProfile.objects.create(user=instance)之后仍然使用instance.profile.save()。看起来每个人都在遵循同样古老的博客!据我所知,create隐式调用save - haccks
8个回答

19

你不应该使用:

user = models.ForeignKey(User, unique=True)

改用这个:

from django.conf import settings
..
user = models.OneToOneField(settings.AUTH_USER_MODEL)

关于OneToOneField,你是正确的,但与用户模型的关系也可以。也就是说,如果您使用附加字段扩展了内置的用户模型。只有在想要用自己的模型替换内置的用户模型时才使用AUTH_USER_MODEL设置。 - allcaps
5
最后一句话有误导性。settings.AUTH_USER_MODEL 是一种保护代码的方式,以防将来硬编码的 User 不再是模型。 - JSmyth

18

我终于想通了。

我忘记在其他UserProfile模型字段中添加 null=True

所以现在accounts.models.UserProfile字段如下:

user = models.ForeignKey(User, unique=True)
birth_date = models.DateField(null=True)
genre = models.CharField(max_length=1, choices=GENRE_CHOICES, null=True)
address = models.CharField(max_length=150, null=True)
postal_code_4 = models.PositiveIntegerField(null=True)
postal_code_3 = models.PositiveIntegerField(null=True)
locatity = models.CharField(max_length=30, null=True)
marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES, null=True)
child_amount = models.PositiveSmallIntegerField(null=True)
is_merchant = models.BooleanField(default=False)
store = models.ForeignKey(Store, null=True)

......一切都按照预期运作!

感谢尝试帮助 Ashray ^^


你能否请检查并指导我一下,关于stackoverflow的问题http://stackoverflow.com/questions/17806683/create-row-of-date-while-creating-superuser。 - user2086641
你好,能否发布一下你的views.py文件长什么样子? - Boidot

6
你可以使用Django信号创建这个功能。 参考 https://docs.djangoproject.com/en/2.2/topics/signals/ 在settings.py中的installed_apps中添加此内容即可。
# settings.py

INSTALLED_APPS = [
    ...
    'accounts.apps.AccountsConfig',
    ...

]

注意:如果您在INSTALLED_APPS中使用了“账户”,则必须在init.py中进行初始化。 在models.py中删除create_user_profile。
# models.py

from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from main.models import Store

class UserProfile(models.Model):

    GENRE_CHOICES = (
        ('m', 'Masculino'),
        ('f', 'Feminino'),
    )
    MARITAL_STATUS_CHOICES = (
        ('s', 'Solteiro'),
        ('c', 'Casado'),
        ('d', 'Divorciado'),
        ('v', 'Viúvo'),
    )

    user = models.OneToOneField(User)
    birth_date = models.DateField()
    genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
    address = models.CharField(max_length=150)
    postal_code_4 = models.PositiveIntegerField()
    postal_code_3 = models.PositiveIntegerField()
    locatity = models.CharField(max_length=30)
    marital_status = models.CharField(max_length=1, choices=MARITAL_STATUS_CHOICES)
    child_amount = models.PositiveSmallIntegerField()
    is_merchant = models.BooleanField(default=False)
    store = models.ForeignKey(Store, null=True)

在 signals.py 中添加 create_user_profile。
# signals.py

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import UserProfile

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


@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

最后,编辑 apps.py 文件。

# apps.py

from django.apps import AppConfig

class AccountsConfig(AppConfig):
    name = 'accounts'

    def ready(self):
        import accounts.signals


1
我喜欢这个答案,应该标记为已解决。谢谢! - dphans
1
你的第三个代码块的结尾应该是instance.userprofile.save()吗?我遇到了一个奇怪的问题,我的.profile版本(或者说.userprofile)导致了AttributeError。 - mr.adam

3
def create_profile(sender,**kwargs ):
    if kwargs['created']:
        user_profile=UserProfile.objects.create(user=kwargs['instance'])


post_save.connect(create_profile,sender=User)

我想这会对你有所帮助。

这需要更多关于为什么有帮助的细节。 - DLAN

1
@receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)
    instance.profile.save()

1
@receiver(post_save, sender=User)
 def save_profile(sender, instance, **kwargs):
     instance.profile.save()

像这样,阅读代码将会更容易。

你能否在这个答案中添加一些解释,使它与其他答案有所区别? - Giulio Caccin

0
    @receiver(post_save, sender=User)
    def create_user_profile(sender, instance, created, **kwargs):
        if kwargs.get('created', True) and not kwargs.get('raw', False):
            Profile.objects.create(user=instance)

谢谢你提供这段代码片段,它可能会提供一些有限的、即时的帮助。一个适当的解释将极大地提高其长期价值,因为它会展示为什么这是一个好的问题解决方案,并使其对未来有类似问题的读者更有用。请[编辑]你的答案,添加一些解释,包括你做出的假设。 - CertainPerformance

0

我尝试了这种方法并取得了成功

def create_profile(sender,**kwargs ):
    if kwargs['created']:
        user_profile=UserProfile(user=kwargs['instance'])
        user_profile.save()

post_save.connect(create_profile, sender=User)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接