django附加额外信息到user

django附加额外信息到user  

django的用户模块提供了很好的用户管理,包括用户的注册和登录认证,但是发现user的模型中只包含了简单的几个数据段,如果我们想要其他的信息,比如用户的兴趣爱好,职业等,我们怎么办呢。

下面我找到一篇的很好的文章提到了,怎么做到这点。

 

Django User Profiles – Simple yet powerful

So you're building a web application, and using the excellent contrib.auth subsystem to manage user accounts. Most probably you need to store additional information about your users, but how? Django profiles to the rescue!

Django provides a lightweight way of defining a
profile object linked to a given user. The profile object can differ from project to project, and it can even handle different profiles for different sites served from the same database.

In a nutshell, using Django profiles consists of 3 steps:

  • Define a model that holds the profile information.
  • Tell Django where to look for the profile object.
  • Create the user profile as needed.

 

Defining the user profile model

The only requirement Django places on this model is that it have a unique ForeignKey to the User model, and is called user. Other than that, you can define any other fields you like.

For our example, we'll create 2 user profile fields (url and company).

account/models.py

from django.db import models
from django.contrib.auth.models import User

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    url = models.URLField("Website", blank=True)
    company = models.CharField(max_length=50, blank=True)

 

Tell Django about the profile object

Accessing a users profile is done by calling user.get_profile(), but in order to use this function, Django needs to know where to look for the profile object.

This is defined in the projects settings.py file.

AUTH_PROFILE_MODULE = "account.UserProfile"

Important note: The settings value is appname.modelname, and not
appname.models.modelname as you might expect. The reason for this is that Django is not doing a direct import, but using an internal model-loading function.

Once defined, and if the profile exists, accessing a users profile is simple.

foo/views.py

@login_required
def view_foo(request):
    user_profile = request.user.get_profile()
    url = user_profile.url

    #OR
    url = request.user.get_profile().url

 

Create the user profile as needed

Notice in the section above I mentioned "if the profile exists". This is because the
get_profile() function will raise a DoesNotExist exception if the profile does not exist.

One of the common solutions to this issue is to create the profile when a user is registered using a signal (see my post on
Django Signals).

Another option is catch the exception, and redirect the user to a profile creation form. You usually want to do this if the profile fields are mandatory.

My personal favorite though, is to have the profile created automatically when referenced, with the added bonus of being able to reference a users profile as
user.profile instead of user.get_profile()

account/models.py

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

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

 

To reiterate, including the above enables us to reference the users profile with cleaner code, and have it created if it does not exist.

foo/views.py

@login_required
def view_foo(request):
    url = request.user.profile.url

 

Ever needed to store additional user information?
Post a comment!

Enjoyed this post? Get future posts
delivered by email or get the RSS feed.

Find us on Facebook and
Twitter.

 
 

Last update: Thu, February 23 – 17:00

 

Comments

Hi Alon, thanks for the

Hi Alon, thanks for the interesting and clear post!

I'm currently trying to write a django application that should be based on more than one user profile.. How would your solution coexist with an abstract base-profile? Subclasses could be handled (and attached) on creation with signals or with the (very nice!)
User.profile function, but what about the base one?

Thank you very much,

cico

Alon Swartz's picture

ForeignKey ?

Thanks for the feedback. I'm not quite sure what you mean, Django User Profiles are a simple way to associate additional information to your user accounts. If you need to associate other models to a user account, then you should use a foreign key.

foo/models.py

from django.db import models
from django.contrib.auth.models import User

class Foo(models.Model):
    user = models.ForeignKey(User)
    stuff = models.CharField(max_length=50)
    ...

You could then refer to the associated user foo's with something like this:

foo/views.py

from django.contrib.auth.decorators import login_required

@login_required
def bar(request):
    foos = request.user.foo_set.all()  # or any of the other useful methods
    ...

I hope the above helps, if not, the more info you can provide, the easier it will be to help.

Why do Django profiles exist

Why do Django profiles exist when you can just define another model with a foreign key to the user model? Is it simply to define a standard profiling system? It seems odd that it re-creates a profile model with a foreign key yet forces you to use a function
to retrieve it (get_profile) instead of just giving you: user.profile.

 

Thanks, Mike

I am sure django is powerful

I am sure django is powerful and quite useful. The applications in “contrib.” package are very tempting.Especially the tools for generating rss and atom feed.But django is not free from problem. It just isn’t effective enough. It lacks a bit on creating
boundary pushing models effectively. Is it possible to use sphinx for our documentation is this tool compatible with django? I have read some where piston is very useful in creating API’s. Is it so?

Liraz Siri's picture

Are you a bot? If so, can you pass the turing test?

Most of the comments that try to take advantage of the Drupal comment external backlink are posted by automated spam bots. We usually delete those immediately. But your response is eerily relevant to the discussion so I suspect you may be a human.

Please don't take offense. As far as I'm concerned you're welcome to join the community even if you are a bot – so long as you can pass my turing test and respond appropriately.

TurnKey welcomes all sentient beings.

Nice. I like when

Nice. I like when administrators or moderators treat comments in such a way.

Well, it has been some time

Well, it has been some time since I have been looking out for something like this. I often indulge in building web applications and I too use the same subsystem contrib.auth for the purpose of managing my accounts. Things are really great, to be honest,
except for the fact that I have to store extra information regarding the users. Anyway, since I started using Django, things have really been great from then on! The fact that it provides a really easy way of defining things is why I found it handy!

creating the profile

Hi Alon,

What is the python-level code for creating users' profiles? In the shell, whenever I type "p=User.objects.get(username='xxxx').get_profile" followed by "p.attribute1", I receive a "'User' object has no attribute 'attribute1'.

Any suggestions for creating the profile to make sure this attribute "registers"? Thanks!

Alon Swartz's picture

get_profile is a function

get_profile is a function, so you need to call it. Using your example you would do something like this:

from django.contrib.auth.models import User

p = User.objects.get(username="xxxx").get_profile()  # notice the ()
print p.attribute1

But, depending on your code and what you want to do, I would recommend re-reading the code snippets in the blog post, and use user.profile.attribute instead, it's more elegant.

follow-up question

Hi again Alon, Thanks for your earlier reply. I'm getting back to Django after a long holiday vacation (I hope you enjoyed one too!), and I revisited your answer.

I think I figured out my problem, and I thought it may be helpful for others if I post this here. Also, I welcome your thoughts on my quick-and-dirty/DIY/band-aid fix.

I realized that setting up profiles according to your instructions doesn't actually create profiles for pre-existing users. So when typing "get_profile()" it always returned an error, even though I
knew the users existed and had attributes.

In response to my own question, I figured out how to create user profiles for pre-existing users. First is assigning the user object: "u=User.objects.get(username="xxxx")". Then you must type: "u.userprofile_set.get_or_create(field1=foo, field2=bar, attribute3=foobar)".
This creates the profile with the assigned field values.

Of course, this could all be done in a view. And it could also be re-coded to match your elegant suggestion. But this at least is how I got it to work code-level.

One question, though: do you know of a way to update the values once they're set?

Thoughts? Suggestions (other than for elegance)?

Thanks again, Alon!

Alon Swartz's picture

Glad you got it sorted

Glad you got it sorted. Take another look at account/models.py in my original post – it will get the profile if it exists or create it when it doesn't.

Regarding updating values, it's a regular model, so once you have the user/profile object, just update the values and call save().

creation error

Thank you for your explanations but what's the way to create a new custom user then ?

I guess I should first create the "normal" user (using User.objects.create_user(,,)) and then the extended one using the foreign key to the newly created user. When I'm using the shell to do it, I've got an error with the first command that the first of
my custom field is required…

Ok forgot what I said, I put

Ok forgot what I said, I put some code I found
here that I guess is wrong

Alon Swartz's picture

I'm not sure I follow your question…

If you want to manually create a user account you could do it like the following snippet, and then extend the user using the profile method described in the post. But, you might want to use the
registration app (and tweak or extend it using
signals) – depending on your use case.

user = User.objects.create_user('username', 'foo@example.com', 'password')

If you have a specific question, or a piece of code isn't working, please post it here together with your models.py as it will be a lot easier to help.

I love the option of creating

I love the option of creating a user profile. It really makes a site look professional, and keeps out the riffraff. I don't develop anything without this option.

DatabaseError

Hello.

I have created user profile, but what about the additional table "maApp_userprofile" in my database ? The DatabaseError (1146, "Table 'myApp.myApp_userprofile' doesn't exist") is occurs. And "manage.py syncdb" does nothing. How this table must be created
?

Nice..

I'll need to check out Django now. Looks simple enough.

still stuck….

Thanks for the useful post!  Where I am still stuck is trying to figure out how to create test users through a command.  For example, if I have an app:

 

accounts

    models.py

    management

         commands

               mycommand.py

 

and I follow your lead in models.py:

 

 

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True)
    age=models.IntgerField(blank=True)
    display_name=models.CharField(max_length=150)

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

 

What would I put in mycommand.py to create a test user with a username of Bob, no age given, and a display name of Bob Smith?  So far, everything I have tried han't actually resulted in the UserProfile which is attached to the user actually being updated
in the database.

 

Thanks,

William

Alon Swartz's picture

Did you save?

You really should look into using a contrib module to handle user creation for you (such as django-registration), but I suppose it depends on your use-case.

Anyway, hopefully the below code snippet will help you get started.

user = User.objects.create_user('bob', 'bob@example.com', 'password')
user.first_name = "Bob"
user.last_name = "Smith"
user.save()

user_profile = user.get_profile()
user_profile.display_name = "Bob Smith"
user_profile.save()

My guess is you forgot to call save() on the object you created, which is required to write the changes to the database.

Also note that I used first_name and last_name as an alternative to "display_name", that's usually how you store account names in Django, you can then access the fullname like this:

user.get_full_name()

I hope the above helps…

back-end

Hey, (sorry for my english :-( )

Thanks for this posts !

I want to add the Field 'Bureau'  the user.

I do the same things in your post ;)

But I don't have the field 'Bureau' in the back-end of the site ??!!

 

Steps to build user accounts

 

Hi Alon,

I've read quite a bit about creating users and it all seems to make sense until I go to make them myself.  I find I am missing some crucial bit of information to make it all work.

Here is what I think I should do to set up a user accounts:

 

1. Create app called 'accounts'

2. In accounts/models.py, create a model for the 'base' user information; username, password, email

3. In accounts/models.py, create a second model for the detailed user information; first & last name, favourite colour, etc.

4. In accounts/views.py, create functions for new user registration, login, logout, etc.

5. In ../settings.py, set AUTH_PROFILE_MODULE = ('accounts.baseUser',)

6. In ../urls.py, link views to templates

7. In ../templates/registration/accounts make template files for login, etc.

 

Is step 2 correct or should this 'base' user model be handled by contrib.auth.models?  In otherwords, do I just need to worry about making models for the extended information and link it to contrib.auth.models.User?

 

You may guess that I am new to Django and obj-oriented programming.  I have gone through the Djangoproject.com.  Can you share a link to an example where someone sets up the user models, registration, login from scratch as opposed to snippits that I have
to figure out how to reassemble?

Alon Swartz's picture

You don't need…

You don't need step 2, actually you must not do anything like step 2 if you want to use contrib.auth

You also don't need to create first/last name fields as you mentioned in step 3 as contrib.auth.models.User already has them. See my
comment above for more details on that.

You also don't need step 4, contrib.auth handles that for you.

Regarding step 5, I'm don't think using a tuple will work. It should be a string, see the post for an example.

I'd recommend re-reading the post a couple of times, and if something isn't clear let me know and I'll elaborate. If something isn't working, feel free to ask and include code snippets.

steps to build user accounts

Thanks, Alon.  Your response helps to clarify.  I will reread.  

Roland

models.ForeignKey(User) Causes Admin Error

Hi Alon,

I got rid of the user models I built before and am now relying on the built in user model.  I built an app called msgboard that has the following model:

 

from django.db import models
from datetime import datetime
from django.contrib.auth.models import User
 
class FindMsg (models.Model):
    msg = models.TextField(max_length=500)
    pub_date = models.DateTimeField('date published')
    author = models.ForeignKey(User)
    def __unicode__(self):
        return self.msg
 
I have my admin site installed and it is working; I can add users.  However, the author = models.ForiegnKey(User) in the model causes the error below when I load admin/msgboard/findmsg in my browser but if I get rid of that line, it works.  Also interesting
is that the 'author' line does not cause admin/msgboard/findmsg/add to fail; ie author shows up as a form item as it should.
 
Here's the browser error:
 
DatabaseError at /admin/msgboard/findmsg/
no such column: msgboard_findmsg.author_id
Request Method: GET
Request URL: http://127.0.0.1.8000/admin/msgboard/findmsg
Django Version: 1.3
Exception Type: Database Error
Exception Value: no such column: msgboard_findmsg.author_id
….
 
Any ideas?
 
Thanks,
Roland

models.ForeignKey(User) Causes Admin Error

Alon,

Very interesting.  I created a new project from scratch to try it again and it worked this time. The problem, I believe, was that I corrupted the original project's admin site by attempting to define my own User models.  I guess this causes irreparable corruption
or, at least, I don't know how to fix it. 

Roland

The only problem with this is

The only problem with this is that there is no profile caching like with get_profile().  So if you were to do something like…

    user.profile.some_field = some_value

    user.profile.save()

the user profile would not be updated!

Django user registration system

I am very new to the Django. Can you help me to design a Django project which will create the user and their profiles. But I dont want to use default admin Django model. And these are accessable without admin login.

auto creation of the UserProfile

What do you think of the ForienKey approach in combonation with a "User" post_save creation of the "UserProfile"? The following app is pluggable and takes care of all of that for you.

Mind you that the UserProfile class still needs to be created by developers and the delete() method on the UserProfile has to be updated to delete any non-user FK, M2M … etc.

This the ForienKey approach recommended by James Bennette the release manager of Django. 

https://github.com/un33k/django-bootup/blob/master/README

I was facing problem of

I was facing problem of creating profile.But your instructions helped me a lot.Thanks.

UserProfiles in a second database

Hello!

I'm building Django app on top of an existing database (MS SQL). I have to access some existing data in that database, so as a UserProfile model I've used an exitsting table. Everything works as expected, but using MSSQL and Apache on Windows drives me to
a lot of problems so I decided to move to Linux + Apache + Postgres database – MyApp specific models will be handled by postgres database, but I still need to have access to for example MSSQL UserProfile table. Is there any way to do that? No success till
now :-(

Regards

Szubi

Displaying profile?

Great info. But was wondering how to create a url to display the users profile along with a way for a user to add their own profile info with a form. Once a form is created from the UserProfile model, what kind of view would be required and accompanying
url for that (would the id be user_id or profile_id)?

I'm trying to incorporate profiles from scratch as django-profiles is quite confusing and I'm kind of stuck at the point where profiles are automatically created when a uuser registers, but I want to not only display these, but also have a user able to input
the detailed profile info on their own through a form.

Any help greatly appreciated as I've been at this for hours.

Displaying profile

 

in urls.py:

(r'^settings/$', user_settings),  
    (r'^settings_changed/$', direct_to_template, {'template':'form_changed.html',
    'extra_context':{'message':'Settings has been changed','next':'/settings/'}}), 

Somewhere in views.py:

@login_required
@csrf_protect
def user_settings(request):
    c = {}
    c.update(csrf(request))
    user = request.user
    profile = user.profile
    if request.method == 'POST': # If the form has been submitted...
        form = UserprofileForm(request.POST,instance=profile) # A form bound to the POST data
        if form.is_valid(): # All validation rules pass
            form.save()
            # Process the data in form.cleaned_data
            # ...
            return HttpResponseRedirect('/settings_changed/') # Redirect after POST
    else:
        user = request.user
        profile = user.profile
        form = UserprofileForm(instance=profile) # An unbound form
    return render_to_response('settings.html', locals(),context_instance=RequestContext(request))

In models.py:

class UserprofileForm(ModelForm):
    class Meta:
        model = UserProfile
        fields = ('show_thumbnails','interface','style') #put fileds You want user to edit here

    def clean(self): # add custom validation here
        cleaned_data = self.cleaned_data
        interface = cleaned_data.get("interface")
        st  = cleaned_data.get("show_thumbnails")
        .........
        if field_interface_does_not_pass_validation_rules:
                # We know these are not in self._errors now (see discussion
                # below).
                msg = u"Invalid input blah blah!!"
                self._errors["interface"] = self.error_class([msg])

                # These fields are no longer valid. Remove them from the
                # cleaned data.
                del cleaned_data["interface"]

        # Always return the full collection of cleaned data.
        return cleaned_data

settings.html:

{% extends "base.html" %}
{% block title %}Title text{% endblock %}
{% block content %}
 
{% if form.errors %}
<p class="error">Invalid input</p>
{% endif %}
<form method="POST" action="/settings/">
{% csrf_token %}
<table class="entryTable">
<tbody>
{{ form.as_table }}
<br><br>
<tr><td class="buttons" colspan="2">
<input type="button" class="button" value="Cancel" onclick="location.href='/0/';"/>
<input type="submit" class="button" value="Submit"/>
</td></tr>
</tbody>
</table>
</form>
{% endblock %}

That all ;-)

Szubi

Your URLs File

@Szubi,

Can you please walk me through the URLs file you mention above, I am not sure I understand what each part does.

Thank you,

dp

using the user profile in a reverse lookup…

Hello Alon and THANK YOU!!!

If I found this post last week it would have saved me about 5 days worth of coding and trobleshooting–THANK YOU!

I am new to Django and I am trying to set up a view that uses a reverse lookup in a url.  How can I pass the the User's ID number to a url so the user moves from one form on his account (User Information) to another form on his account where he inputs information
directly related to his account? 

#views.py

def regPage(request):
    form = RegForm(request.POST)
    # Save new/edited pick
    if request.method == 'POST' and form.is_valid():
        form.save()
        return HttpResponseRedirect(reverse('league_page', args=(), kwargs={ 'id' : id }))
        #return HttpResponseRedirect('/dev/leaguepage/')
    user_info = UserRegistration.objects.all()
    context = {
        'form':form,
        'user_info' :user_info,
    }

    return render(request, 'regpage.html', context)

def leaguePage(request, id):

    form = LeagueForm(request.POST or
None instance=id and LeagueRegistration.objects.get(id=id))

    # Save new/edited pick

    if request.method == 'POST'
and form.is_valid(): form.save() return HttpResponseRedirect('/draft/') league_info = LeagueRegistration.objects.all() context = {

        'form':form, 'league_info' :league_info,

    return render(request,
'leaguepage.html', context)

#urls.py

urlpatterns = patterns('',

  

    #url(r'^leaguepage/$','acme.dc_django.views.leaguePage'),

    url(r'^user/(?P<id>d+)/$','acme.dc_django.views.leaguePage', name="league_page"),

    url(r'^$', 'acme.dc_django.views.regPage'),

)

Please not the above code doesn't work.  This is the code I have been troubleshooting.

Thank you,

 

dp

 

版权所有,禁止转载. 如需转载,请先征得博主的同意,并且表明文章出处,否则按侵权处理.

    分享到: