I have an extended UserProfile model in django:
class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)
#other things in that profile
And a 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)
I make sure the signal gets registered by having this in my __init__.py
:
import signals
So that should create me a new UserProfile for every user that registers, right? But it doesn't. I always get "UserProfile matching query does not exist" errors when I try to log in, which means that the database entry isn't there.
I should say that I use django-registration, which provides the user_registered signal.
The structure of the important apps for this is, that I have one application called "users", there I have: models.py, signals.py, urls.py and views.py (and some other things which shouldn't matter here). The UserProfile class is defined in models.py.
Update: I changed the signals.py to:
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)
But now I get a "IntegrityError":
"column user_id is not unique"
Edit 2:
I found it. Looks like somehow I registred the signal twice. The workaround for this is described here: http://code.djangoproject.com/wiki/Signals#Helppost_saveseemstobeemittedtwiceforeachsave
I had to add a dispatch_uid, now my signals.py looks like this and is working:
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")
You can implement it using post_save on the user:
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)
Edit:
Another possible solution, which is tested and works (I'm using it on my site):
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)
You can get the extended profile to be created when first accessed for each user instead:
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])
then use
from django.contrib.auth.models import User
user = User.objects.get(pk=1)
user.profile.additional_info_field
ref: http://www.codekoala.com/blog/2009/quick-django-tip-user-profiles/
This helped me: 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")
When you call profile.setUser()
, I think you want to pass instance
rather than sender
as the parameter.
From the documentation of the user_registered signal, sender
refers to the User
class; instance
is the actual user object that was registered.
According to my latest research, creating a separate file, e.g., singals.py, does not work.
You'd better connect 'create_profile' to 'post_save' in your models.py directly, otherwise this piece of code won't be executed since it's in a separate file and no one imports it.
My final code for your reference:
# 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")
Update for 2018:
This question has collected a lot of views, maybe it is time for an update.
This is the latest version for latest 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)
来源:https://stackoverflow.com/questions/1910359/creating-a-extended-user-profile