Custom User Profiles + Signals

126 views
Skip to first unread message

Jeremiah

unread,
Mar 1, 2011, 6:50:37 PM3/1/11
to Django users
Hi All,

I'm going through the help document (http://docs.djangoproject.com/en/
1.2/topics/auth/#storing-additional-information-about-users) and I'm
starting to figure out how signals work. What I'm having problems
with is getting my signal to trigger (or at least figuring out if the
signal is actually triggering?)

So, if I do the following from the shell (as spawned by manage.py):
>>> import cc.models
>>> from django.contrib.auth.models import User
>>> u = User.objects.get(username__exact="duh3")
>>> try:
... u.userprofile
... except:
... profile = cc.models.UserProfile()
... profile.user = u
... profile.thing = "Test"
... profile.save()
...
<UserProfile: duh3>
>>> u.userprofile.user
<User: duh3>
>>> u.userprofile.thing
u'Test'

I can get the profile to work. So, then I add the following lines to
my "models.py" file:
from django.db.models.signals import post_save
...
def profile_handler(sender, **kwargs):
""" Signal handler to deal with a save on the User model """
try:
sender.userprofile
except:
profile = cc.models.UserProfile()
profile.user = sender
profile.save()

post_save.connect(profile_handler, sender=User)

But, what i can't tell is if anything is happening. If I create a new
user and then try userinstance.userprofile, I get the expected
exception.

Could someone please point me at my issue?

Thanks,
Jeremiah

Stefano

unread,
Mar 1, 2011, 8:07:47 PM3/1/11
to django...@googlegroups.com, Jeremiah
why you don't create the profile only if needed ?

class UserProfile(models.Model):
user = models.ForeignKey(User, unique=True)

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

2011/3/2 Jeremiah <wanderi...@gmail.com>:

> --
> You received this message because you are subscribed to the Google Groups "Django users" group.
> To post to this group, send email to django...@googlegroups.com.
> To unsubscribe from this group, send email to django-users...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/django-users?hl=en.
>
>

bruno desthuilliers

unread,
Mar 2, 2011, 7:02:01 AM3/2/11
to Django users
On 2 mar, 00:50, Jeremiah <wanderinweez...@gmail.com> wrote:
> Hi All,
>
> So, if I do the following from the shell (as spawned by manage.py):
> >>> import cc.models

What does cc.models.UserProfile looks like ?

> >>> from django.contrib.auth.models import User
> >>> u = User.objects.get(username__exact="duh3")
> >>> try:
>
> ...     u.userprofile
> ... except:

This should be :

... except cc.models.UserProfile.DoesNotExist:


As a general rule:
* NEVER use a bare except clause unless you reraise the SAME exception
in the except block.
* always catch the most specific exception type.


> ...     profile = cc.models.UserProfile()
> ...     profile.user = u
> ...     profile.thing = "Test"
> ...     profile.save()
> ...

(snip)

> I can get the profile to work.  So, then I add the following lines to
> my "models.py" file:
> from django.db.models.signals import post_save
> ...
> def profile_handler(sender, **kwargs):
>         """ Signal handler to deal with a save on the User model """
>         try:
>                 sender.userprofile
>         except:
>                 profile = cc.models.UserProfile()

Given the above statement, I assume the "models.py" file you're
talking about is not the one where you define your UserProfile class.
If yes - you may have legitimate reasons to split this between
different apps -, are you sure the models.py file where you set up
this signal handler is correctly registered and imported ? (hint: is
the app containing this models.py in your settings.INSTALLED_APPS ?)

FWIW, you can insert print statements (poor man logging) in your
models.py file to make sure it's imported and check if the signal
handler is executed:

# wherever/models.py
from django.db.models.signals import post_save

print "in %s" % __name__

def profile_handler(sender, **kwargs):
""" Signal handler to deal with a save on the User model """
try:
p = sender.userprofile
print "in %s : for user %s, profile %s already exists" %
(__name__, sender, p)
except cc.models.UserProfile.DoesNotExist:
profile = cc.models.UserProfile(user=sender)
profile.save()
print "in %s : for user %s, created profile %s " % (__name__,
sender, p)
except Exception, e:
print "in %s : for user %s, got unexpected exception %s" %
(__name__, sender, e)
raise

print "in %s : connecting profile_handler to post_save signal" %
__name__
post_save.connect(profile_handler, sender=User)
print "in %s : connected profile_handler to post_save signal" %
__name__


Running this with the builtin dev server should provide some useful
informations about your problem.

<OT>While we're at it, the pythonic convention for indentations is 4-
spaces, no tabs</OT>

HTH

Wandering Weezard

unread,
Mar 2, 2011, 1:18:39 PM3/2/11
to django...@googlegroups.com
I'll give that a shot.  Thank you for your feedback. Still learning a lot as I go.

Jeremiah

unread,
Mar 2, 2011, 1:31:33 PM3/2/11
to Django users
I appreciate all of your feedback! My comments inserted within the
message below:


> What does cc.models.UserProfile looks like ?
class UserProfile(models.Model):
user = models.OneToOneField(User)
thing = models.CharField(max_length=200)

def __unicode__(self):
return unicode(self.user)


> This should be :
>
>   ... except cc.models.UserProfile.DoesNotExist:
>
> As a general rule:
> * NEVER use a bare except clause unless you reraise the SAME exception
> in the except block.
> * always catch the most specific exception type.

Noted! Thank you for your guidelines.


> Given the above statement, I assume the "models.py" file you're
> talking about is not the one where you define your UserProfile class.
> If yes - you may have legitimate reasons to split this between
> different apps -, are you sure the models.py file where you set up
> this signal handler is correctly registered and imported ? (hint: is
> the app containing this models.py in your settings.INSTALLED_APPS ?)
It is the same models.py file with the UserProfile class. Is this the
incorrect way to set this up? I have a feeling I'm completely missing
something.

I have it registered under INSTALLED_APPS using the name of the
project I created with startproject (myproject for the sake of
example) and the name of the app via startapp:
...,
'myproject.cc',
...,
Thanks! I didn't know you could do that - I thought it would be
gobbled up as invalid output by the http server so I hadn't tried.

> <OT>While we're at it, the pythonic convention for indentations is 4-
> spaces, no tabs</OT>
Thank you for this as well! I'm both new to Django and Python -
learning as I go.

bruno desthuilliers

unread,
Mar 2, 2011, 2:46:50 PM3/2/11
to Django users
On 2 mar, 19:31, Jeremiah <wanderinweez...@gmail.com> wrote:

(snip)
>> Given the above statement, I assume the "models.py" file you're
>> talking about is not the one where you define your UserProfile class.
(snip)

> It is the same models.py file with the UserProfile class.  Is this the
> incorrect way to set this up?  I have a feeling I'm completely missing
> something.

If it's in the same module (=> .py file), then you don't have to
reference it as "cc.models.UserProfile" - "UserProfile" is enough.

IOW:

def profile_handler(sender, **kwargs):
""" Signal handler to deal with a save on the User model """
try:
sender.userprofile
except UserProfile.DoesNotExist:
profile = UserProfile(user=sender)
profile.save()


> I have it registered under INSTALLED_APPS using the name of the
> project

Don't. It's not necessary, and may break if for any reason you either
rename your project or try to reuse the same app in another project.
Also and FWIW, there are (complicated) issues with modules / packages
qualified names and the way django import models at startup, that may
end up with your module being imported twice under two different
names.

The only potential problem with removing the "project" namespace from
your settings.INSTALLED_APPS *and* imports is that you'll have to add
both the project's parent dir and the project's dir in your sys.path
(in the .wsgi file) when deploying using mod_wsgi.
It's invalid when running django behind a front-end webserver like
Apache but works fine with the builtin dev server.

The RightThingToDo(tm) is of course to use Python's logging package,
but it's a bit more involved.

> > <OT>While we're at it, the pythonic convention for indentations is 4-
> > spaces, no tabs</OT>
>
> Thank you for this as well!  I'm both new to Django and Python -
> learning as I go.

comp.lang.py is a very newbie-friendly place (well, it's a very
friendly place, period), with quite a few gurus hanging around. I
strongly suggest you post your "pure-python" questions there. I
learned quite a few thangs lurking there ;)

Jeremiah

unread,
Mar 2, 2011, 4:27:35 PM3/2/11
to Django users
So continuing with this... I got the print-outs to work (I still need
to try Stefano's suggestion)... I register a new user (duh6) via my
register page and in the output for the dev server, I see:
in myproject.cc.models: for user <class
'django.contrib.auth.models.User'>, pr
ofile <django.db.models.fields.related.SingleRelatedObjectDescriptor
object at 0
x0000000002F031D0> already exists
in cc.models: for user <class 'django.contrib.auth.models.User'>,
profile <djang
o.db.models.fields.related.SingleRelatedObjectDescriptor object at
0x0000000002F
031D0> already exists

So, it seems like the user has a profile. But, if I fire-up a shell
via manage.py and do the following:
>>> from django.contrib.auth.models import User
>>> import cc.models
in cc.models
in cc.models: connected profile_handler to post_save signal
>>> u = User.objects.get(username__exact="duh6")
>>> u
<User: duh6>
>>> u.email
u'du...@duh6.com'
>>> u.userprofile
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "c:\python27\lib\site-packages\django\db\models\fields
\related.py", line
226, in __get__
rel_obj = self.related.model._base_manager.using(db).get(**params)
File "c:\python27\lib\site-packages\django\db\models\query.py", line
347, in g
et
% self.model._meta.object_name)
DoesNotExist: UserProfile matching query does not exist.
>>> u.get_profile()
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "c:\python27\lib\site-packages\django\contrib\auth\models.py",
line 373,
in get_profile
self._profile_cache =
model._default_manager.using(self._state.db).get(user_
_id__exact=self.id)
File "c:\python27\lib\site-packages\django\db\models\query.py", line
347, in g
et
% self.model._meta.object_name)
DoesNotExist: UserProfile matching query does not exist.
>>>

Am I trying to access the profile incorrectly? Not getting something?
All of the above? :)
Reply all
Reply to author
Forward
0 new messages