创建扩展用户配置文件

46

我在Django中有一个扩展的UserProfile模型:

class UserProfile(models.Model):
  user = models.ForeignKey(User, unique=True)
  #other things in that profile

还有一个signals.py:

from registration.signals import user_registered
from models import UserProfile
from django.contrib.auth.models import User

def createUserProfile(sender, instance, **kwargs):
  profile = users.models.UserProfile()
  profile.setUser(sender)
  profile.save()

user_registered.connect(createUserProfile, sender=User)

我通过在__init__.py中加入以下代码来确保信号被注册:

import signals

那么这应该为每个注册的用户创建一个新的UserProfile,对吧?但事实并非如此。每当我尝试登录时,我总是会收到“UserProfile匹配查询不存在”的错误,这意味着数据库条目不存在。

我应该说明一下,我使用django-registration,它提供了user_registered信号。

对于这个问题,重要的应用程序结构是,我有一个名为“users”的应用程序,在那里我有:models.py、signals.py、urls.py和views.py(以及其他一些在这里不重要的东西)。UserProfile类在models.py中定义。

更新:我将signals.py更改为:

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

def create_profile(sender, **kw):
    user = kw["instance"]
    if kw["created"]:
        profile = UserProfile()
        profile.user = user
        profile.save()

post_save.connect(create_profile, sender=User)

但是现在我遇到了一个"完整性错误(IntegrityError)":

"列user_id不是唯一的"

编辑2:

我找到原因了。看起来我以某种方式注册了两次信号(signal)。这个问题的解决方法在这里描述: http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave

我需要添加一个dispatch_uid,现在我的signals.py看起来像这样并且能够正常工作:

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

def create_profile(sender, **kw):
    user = kw["instance"]
    if kw["created"]:
        profile = UserProfile(user=user)
        profile.save()

post_save.connect(create_profile, sender=User, dispatch_uid="users-profilecreation-signal")

你能发布一下你的Django应用程序结构吗?我对你代码中的一些行比如profile=user.models.UserProfile()很好奇 - 你有一个名为'user'的模块吗?UserProfile()在哪里? - czarchaic
这是用户的问题,我不知道那个错别字是怎么出现的,但问题是一样的。我想知道为什么Python没有为拼写错误的路径抛出错误。 - Kai
谢谢您提供的解决方案,我是 Django 的新手,不知道如何保存用户配置文件中其他的数据。我看到您只是将用户保存在 UserProfile 模型中,但是使用 signals.py 从注册表单中如何保存其他数据呢?谢谢(抱歉我的英语)。 - Asinox
6个回答

31

您可以在用户上使用post_save实现它:

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

def create_profile(sender, **kwargs):
    user = kwargs["instance"]
    if kwargs["created"]:
        profile = users.models.UserProfile()
        profile.setUser(sender)
        profile.save()

post_save.connect(create_profile, sender=User)

编辑:
另一个可能的解决方案已经经过测试并可行(我在我的网站上正在使用它):

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
def create_profile(sender, **kwargs):
    user = kwargs["instance"]
    if kwargs["created"]:
        up = UserProfile(user=user, stuff=1, thing=2)
        up.save()
post_save.connect(create_profile, sender=User)

2
必须是"profile.setUser(user)",我认为。但是我得到了“列user_id不唯一”的错误。你有什么想法吗? - Kai
我在我的答案中添加了另一个可能的解决方案,请查看。 - Agos
我发现出现了“column user_id is not unique”错误,请问你能给我看看你的UserProfile模型吗?也许是我在ForeignKey()上出了一些问题。 - Kai
搞定了。如果你对解决方案感兴趣,请查看我的初始帖子。 - Kai
2
@Agos 如果能描述一下在 create_profile 中如何获取 stuffthing 的值,那将非常有帮助。 - veddermatic
我只需在我的个人资料模型上使所有字段default=''blank=True,这样就不需要传入任何参数或stuff=1和thing=2 @veddermatic。 - KhoPhi

6

这个对我有帮助:primary_key=True


class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True, primary_key=True, related_name="user")
    phone = models.CharField(('phone'),max_length=30, blank=False, null=True)
    user_building = models.ManyToManyField(Building, blank=True)
    added_by = models.ForeignKey(User, blank=True, null=True, related_name="added")

3
primary_key=True 表示 null=False 和 unique=True。 - flyingfoxlee

6
您可以在首次访问每个用户时创建扩展配置文件:
from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    additional_info_field = models.CharField(max_length=50)

User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])

然后使用。
from django.contrib.auth.models import User
user = User.objects.get(pk=1)
user.profile.additional_info_field

参考:http://www.codekoala.com/blog/2009/quick-django-tip-user-profiles/

这篇文章介绍了在Django中创建用户个人资料的方法。通过扩展用户模型,可以添加自定义字段来存储更多用户信息。同时,使用信号(signal)可以确保每当新用户创建时都会创建一个对应的个人资料。

5
当您调用profile.setUser()时,我认为您想将instance作为参数传递,而不是sender
user_registered信号的文档中,sender指的是User类;而instance则是已注册的实际用户对象。

1
根据我的最新研究,创建一个单独的文件(例如signals.py)是行不通的。
最好直接将“create_profile”连接到您的models.py中的“post_save”,否则由于它在一个单独的文件中且没有人导入它,这段代码将不会被执行。
供您参考的最终代码:
# models.py

# Here goes the definition of class UserProfile.
class UserProfile(models.Model):
    ...

# Use signal to automatically create user profile on user creation.

# Another implementation:
# def create_user_profile(sender, **kwargs):
#     user = kwargs["instance"]
#     if kwargs["created"]:
#         ...
def create_user_profile(sender, instance, created, **kwargs):
    """
    :param sender: Class User.
    :param instance: The user instance.
    """
    if created:
        # Seems the following also works:
        #   UserProfile.objects.create(user=instance)
        # TODO: Which is correct or better?
        profile = UserProfile(user=instance)
        profile.save()

post_save.connect(create_user_profile,
                  sender=User,
                  dispatch_uid="users-profilecreation-signal")

0

2018年更新:

这个问题已经得到了很多的浏览量,也许是时候进行更新了。

这是最新版本的最新Django。

from django.dispatch import receiver
from django.db.models.signals import post_save
from django.conf import settings
from models import UserProfile

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

我认为(对于2018年,django >=1.11),它应该始终是:sender=get_user_model()。https://docs.djangoproject.com/en/2.0/topics/auth/customizing/#django.contrib.auth.get_user_model - Paolo
UserProfile.objects.create(user=instance) 应该改为 UserProfile.objects.create(user=instance, **kwargs) 吗? - alphazwest
@theeastcoastwest 不是的。**kwargs与信号本身有关,它不包含您在创建配置文件时想要使用的任何数据。实际上,您只需要创建一个干净的UserProfile模型,而不做任何更改。 - aherok
有没有一个资源可以阅读关于使用信号来创建相关对象(例如在这种情况下的UserProfile)与使用模型管理器的区别?他们的方法的优缺点和最佳实践建议?谢谢。 - Deep

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