Accessing objects parent property in django profiles - django

I am using django profiles with having different types of profiles.
My Profile Model of Company is similar to:
from django.db import models
from django.contrib.auth.models import User
from accounts.models import UserProfile
from jobs.models import City
from proj import settings
from django.core.mail import send_mail
class Company(UserProfile):
name=models.CharField(max_length=50)
short_description=models.CharField(max_length=255)
tags=models.CharField(max_length=50)
profile_url=models.URLField()
company_established=models.DateField(null=True,blank=True)
contact_person=models.CharField(max_length=100)
phone=models.CharField(max_length=50)
address=models.CharField(max_length=200)
city=models.ForeignKey(City,default=True)
def send_activation_email(self):
self.set_activation_key(self.username)
email_subject = 'Your '+settings.SITE_NAME+' account confirmation'
email_body = """Hello, %s, and thanks for signing up for an
example.com account!\n\nTo activate your account, click this link within 48
hours:\n\nhttp://"""+settings.SITE_URL+"/accounts/activate/%s""" % (self.username,self.activation_key)
send_mail(email_subject,
email_body,
'acccounts#site.com',
[self.email])
Here I am inheriting Company from UserProfile and in send activation email, I am trying to use properties and methods of parent classes user and userprofile. Its direct parent is userprofile while user has onetoone relation with userprofile. So I tried to call self.activation_key that is defined in userpofile and called self.username that is property of user class/table and getting error on self.username .
So seems like I am calling self.username in wrong way,so how can I access self.username ? Any detail will be helpful and appreciated.

Company inherits from UserProfile, but as you say UserProfile doesn't inherit from User - it has a OneToOne relationship with it. So there's no such thing as self.username - there's simply a relationship from self to User.username. I'm going to guess that that relationship is via a field called user, in which case you want self.user.username.

Related

Extending User profile in Django 1.7

I know, this question has been already asked many times in SO, but most of the answers I read were either outdated (advising the now deprecated AUTH__PROFILE_MODULE method), or were lacking of a concrete example.
So, I read the Django documentation [1,2], but I lack a real example on how to use it properly.
In fact, my problem comes when a new user is created (or updated) through a form. The user is obviously created but, the fields from the extension are all unset. I know that the Django documentation is stating that:
These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a django.db.models.signals.post_save could be used to create or update related models as appropriate.
But, I don't know how to do it in practice (should I add a a receiver and if 'yes', which one).
For now, I have the following (taken from the documentation for the sake of brevity):
File models.py
from django.contrib.auth.models import User
class Employee(models.Model):
user = models.OneToOneField(User)
department = models.CharField(max_length=100)
File admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import Employee
# Define an inline admin descriptor for Employee model
class EmployeeInline(admin.StackedInline):
model = Employee
can_delete = False
verbose_name_plural = 'employee'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (EmployeeInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
File forms.py
class SignupForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
class SettingsForm(account.forms.SignupForm):
department = forms.CharField(label="Department", max_length=100)
Then, in my code, I use it like this:
u = User.objects.get(username='fsmith')
freds_department = u.employee.department
But, Signup and Settings forms do not operates as expected and new values for the departement is not recorded.
Any hint is welcome !
I have looked at all the answers but none does really hold the solution for my problem (though some of you gave me quite good hints for looking in the right direction). I will summarize here the solution I have found to solve my problem.
First of all, I have to admit I didn't tell everything about my problem. I wanted to insert extra fields in the User model and use other apps such as the default authentication scheme of Django. So, extending the default User by inheritance and setting AUTH_USER_MODEL was a problem because the other Django applications were stopping to work properly (I believe they didn't use user = models.OneToOneField(settings.AUTH_USER_MODEL) but user = models.OneToOneField(User)).
As, it would have been too long to rewrite properly the other applications I am using, I decided to add this extra field through a One-to-One field. But, the documentation miss several points that I would like to fill in the following.
So, here is a complete example of adding an extra field to the User model with other applications using the same model.
First, write the description of the model gathering the extra fields that you want to add to your models.py file:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
extra_field = models.CharField(max_length=100)
Then, we need to trigger the addition of an object UserProfile each time a User is created. This is done through attaching this code to the proper signal in the receiver.py file:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from my_user_profile_app.models import UserProfile
#receiver(post_save, sender=User)
def handle_user_save(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
Now, if you want to be able to modify it through the administration interface, just stack it with the usual UserAdmin form in the admin.py file.
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User
from my_user_profile_app.models import UserProfile
# Define an inline admin descriptor for UserProfile model
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Then, it is time now to try to mix this extra field with the default Django authentication application. For this, we need to add an extra field to fill in the SignupForm and the SettingsForm through inheritance in the forms.py file:
import account.forms
from django import forms
class SignupForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
class SettingsForm(account.forms.SignupForm):
extra_field = forms.CharField(label="Extra Field", max_length=100)
And, we also need to add some code to display and get properly the data that you have been added to the original User model. This is done through inheritance onto the SignupView and the SettingsView views in the views.py file:
import account.views
from my_user_profile_app.forms import Settings, SignupForm
from my_user_profile_app.models import UserProfile
class SettingsView(account.views.SettingsView):
form_class = SettingsForm
def get_initial(self):
initial = super(SettingsView, self).get_initial()
initial["extra_field"] = self.request.user.extra_field
return initial
def update_settings(self, form):
super(SettingsView, self).update_settings(form)
profile = self.request.user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
class SignupView(account.views.SignupView):
form_class = SignupForm
def after_signup(self, form):
profile = self.created_user.userprofile
profile.extra_field = form_cleaned_data['extra_field']
profile.save()
super(SignupView, self).after_signup(form)
Once everything is in place, it should work nicely (hopefully).
I struggled with this topic for about a year off and on until I finally found a solution I was happy with, and I know exactly what you mean by "there is a lot out there, but it doesn't work". I had tried extending the User model in different ways, I had tried the UserProfile method, and some other 1-off solutions as well.
I finally figured out how to simply extend the AbstractUser class to create my custom user model which has been a great solution for many of my projects.
So, let me clarify one of your comments above, you really shouldn't be creating a link between 2 models, the generally accepted "best" solution is to have one model which is inherited from AbstractUser or AbstractBaseUser depending on your needs.
One tricky thing that got me was that "Extending the User Model" did not get me where I wanted and I needed to Substitute the User Model, which I'm sure you've seen/read multiple times, but possibly not absorbed it (at least I know I didn't).
Once you get the hang of it, there's really not that much code and it's not too complicated either.
# models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
'''
Here is your User class which is fully customizable and
based off of the AbstractUser from auth.models
'''
my_custom_field = models.CharField(max_length=20)
def my_custom_model_method(self):
# do stuff
return True
There are a couple things to look out for after this, some of which came up in django 1.7.
First of all, if you want the admin page to look like it did before, you have to use the UserAdmin
# admin.py
from django.contrib import admin
from django.contrib.auth import get_user_model
from django.contrib.auth.admin import UserAdmin
# Register your models here.
admin.site.register(get_user_model(), UserAdmin)
The other thing is that if you're wanting to import the User class in a models file, you have to import it from the settings and not with get_user_model(). If you run into this, it's easy to fix, so I just wanted to give you a heads up.
You can check out my seed project I use to start projects to get a full but simple project that uses a Custom User Model. The User stuff is in the main app.
From there all the Registration and Login stuff works the same way as with a normal Django User, so I won't go into detail on that topic. I hope this helps you as much as it has helped me!
I try to avoid to extend the user model as explained in the django docs.
I use this:
class UserExtension(models.Model):
user=models.OneToOneField(User, primary_key=True)
... your extra model fields come here
Docs of OneToOneField: https://docs.djangoproject.com/en/1.7/topics/db/examples/one_to_one/
I see these benefits:
the same pattern works for other models (e.g. Group)
If you have N apps, every app can extend the model on his own.
Creating the UserExtension should be possible without giving parameters. All fields must have sane defaults.
Then you can create a signal handler which creates UserExtension instances if a user gets created.
I prefer extend the User model. For example:
class UserProfile(User):
def __unicode__(self):
return self.last_name + self.first_name
department = models.CharField(max_length=100)
class SignupForm(forms.Form):
username = forms.CharField(max_length=30)
first_name = forms.CharField(max_length=30)
last_name = forms.CharField(max_length=30)
department = forms.CharField(label="Department", max_length=100)
To save the data
form = UserRegistrationForm(request.POST)
if form.is_valid():
client = UserProfile()
client.username = username
client.set_password(password)
client.first_name = first_name
client.department = department
client.save()
check how are you saving the data after validate the form

Django: Issues with extending User model

I'm trying to add fields to the User model and add them to the admin page. There is a recommended method in the django docs here:
https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
So, I created a OneToOne field for my new model:
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
designs = models.ManyToManyField('Design', blank=True)
prints = models.ManyToManyField('Print', blank=True)
rating = models.IntegerField(null=True, blank=True)
reliability = models.IntegerField(null=True, blank=True)
av_lead_time = models.IntegerField(null=True, blank=True)
Added an AUTH_PROFILE_MODULE to settings.py:
AUTH_PROFILE_MODULE = 'website.UserProfile'
Tried to add the UserProfile fields to the admin page:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from website.models import UserProfile
from django.contrib.auth.models import User
# Define an inline admin descriptor for UserProfile model
# which acts a bit like a singleton
class UserProfileInline(admin.StackedInline):
model = UserProfile
can_delete = False
verbose_name_plural = 'profile'
# Define a new User admin
class UserAdmin(UserAdmin):
inlines = (UserProfileInline, )
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
Now, when I try to access a registered user via the admin menu, I get:
Caught DoesNotExist while rendering: User matching query does not exist.
In template /usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
19 {{ field.field }}
And when I try to add a new user via the admin menu, I get:
Caught DoesNotExist while rendering: User matching query does not exist.
In template /usr/local/lib/python2.7/dist-packages/django/contrib/admin/templates/admin/includes/fieldset.html, error at line 19
19 {{ field.field }}
Why doesn't it recognise that particular field?
Edit: After looking on the full error message I can see the error is not solely related to extending User. The error happens when rendering checkboxes and corresponding labels that are used to assign prints to UserProfile you are editing/adding. Django admin is calling Print.__unicode__ for rendering label for each Print instance, which in turn access (on line 33 of /threedee/website/models.py) the Print's "printer" attribute which is a foreign key to User. And for some reason one of the Prints does have invalid printer value which doesn't point to any User.
Can't really tell what is really happening here without seeing the Print model, I recommend you checking the Print database table (should be named website_print) and find if there is anything unusual (are you using PostgreSQL?). If you are not having any important data there, truncating whole Print table should do the trick.
This is my old answer which you should still follow but it's not related to the error you are experiencing:
I would just comment on others answers but there doesn't seem to be a way of doing that for me. You need to combine both Alexey Sidorov's and Like it's answers:
First use django shell to create UserProfile instances for existing users - just run commads provided by Like it's answer in the shell (python manage.py shell).
After that you should setup signal that will automatically create UserProfile for each new user according to answer from alexey-sidorov.
Add this to models.py, after UserProfile class.
from django.db.models.signals import post_save
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
more info https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users
Add UserProfile entry for you existing users:
from django.contrib.auth.models import User
from website.models import UserProfile
for user in User.objects.all():
profile = UserProfile.objects.get_or_create(user = user)
and create profile for new user as well.
use signals for what.

How to correctly create the "User extra info" object. / IntegrityError "Column *something* cannot be null"

In order to add informations to my auth.User model I followed this piece of doc.
I would like to link a User to a Society (assuming a user is a client for example) and to a job in this society.
So here's my code:
societies/models.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
"""
Stores the user informations except
login basic informations.
"""
user = models.OneToOneField(User)
firstname = models.CharField(max_length=128)
lastname = models.CharField(max_length=128)
society = models.ForeignKey('Society')
job = models.ForeignKey('UserJob')
class Society(models.Model):
"""
Stores the informations about the societies.
"""
name = models.CharField(max_length=128)
class UserJob(models.Model):
"""
Stores the user job category in the society.
"""
name = models.CharField(max_length=64)
def create_user_profile(sender, instance, created, **kwargs):
"""
Uses the post_save signal to link the User saving to
the UserProfile saving.
"""
if created:
UserProfile.objects.create(user=instance)
#the instruction needed to use the post_save signal
post_save.connect(create_user_profile, sender=User)
societies/admin.py:
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
from societies.models import UserProfile, Society, UserJob
class UserProfileInline(admin.StackedInline):
"""
Defines an inline admin descriptor for UserProfile model
which acts a bit like a singleton
"""
model = UserProfile
can_delete = False
verbose_name_plural = 'profile'
class UserAdmin(UserAdmin):
"""
Defines a new User admin.
"""
inlines = (UserProfileInline, )
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
admin.site.register(Society)
admin.site.register(UserJob)
I added the UserProfileInline in order to have the fields on my admin-site User form.
I added this line to my settings.py:
AUTH_PROFILE_MODULE = 'societies.UserProfile'
The problem is then that when I try to create a User through my User form on the admin-site, including filling the UserProfile-specific fields, I get this:
IntegrityError at /admin/auth/user/add/
(1048, "Column 'society_id' cannot be null")
Because I've specified a society on the form I'm asking my self if the problem is not coming from the signal handling with my create_user_profile function. Considering the doc I mentionned earlier, there is nothing more to do. However, don't I have to precise how to fill the UserProfile fields firstname, lastname, society and job through the UserProfile.create(...) call ? (in addition to the "user=instance" parameter). In this case, I don't know how to get the right elements to fill the parameters. In the doc nothing is done concerning the "accepted_eula" and "favorite_animal" so I'm certainly wrong... isn't it ?
Thank you so much for any response.
I apologize for my language.
I found my mistake, I had to add a default value to the Society and UserJob foreignkey fields in the UserProfile model. Another solution would be to specify they can be null.
Sorry for this lack of attention.

Django User Authentication

I am trying to set up user authentication for my django project but I keep getting a database error UserProfile_User does not exist. I have tried most of the examples online but none have solved the issue. Below is the code I'm currently trying out. Any relevant pointers would be greatly appreciated. user profile model that I'm currently trying out.
from datetime import datetime
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
dob = models.DateField(default=datetime.today().year - 18)
def __unicode__(self):
return ('%s' % (self.user.username))
Using a OneToOneField is fine in this case but that doesn't mean that the profile will be created for you. It's fairly simple to have the profile created by a signal:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
def user_post_save(sender, instance, created, **kwargs):
# Creates user profile
if created:
profile, new = UserProfile.objects.get_or_create(user=instance)
post_save.connect(user_post_save, sender=User)
This would be included in your models.py just below the UserProfile definition and will ensure that all of your users have a profile associated with them.
I never use OneToOneField, but my guess is, that its is requiring a bidirectional existence of both (user and its profile). If one of both does not exist it will raise an Exception. Maybe you are better of with a ForeignKey.

Reorder users in django auth

I have a model that has a ForeignKey to the built-in user model in django.contrib.auth and I'm frustrated by the fact the select box in the admin always sorts by the user's primary key.
I'd much rather have it sort by username alphabetically, and while it's my instinct not to want to fiddle with the innards of Django, I can't seem to find a simpler way to reorder the users.
The most straightforward way I can think of would be to dip into my Django install and add
ordering = ('username',)
to the Meta class of the User model.
Is there some kind of monkeypatching that I could do or any other less invasive way to modify the ordering of the User model?
Alternatively, can anyone thing of anything that could break by making this change?
There is a way using ModelAdmin objects to specify your own form. By specifying your own form, you have complete control over the form's composition and validation.
Say that the model which has an FK to User is Foo.
Your myapp/models.py might look like this:
from django.db import models
from django.contrib.auth.models import User
class Foo(models.Model):
user = models.ForeignKey(User)
some_val = models.IntegerField()
You would then create a myapp/admin.py file containing something like this:
from django.contrib.auth.models import User
from django import forms
from django.contrib import admin
class FooAdminForm(forms.ModelForm):
user = forms.ModelChoiceField(queryset=User.objects.order_by('username'))
class Meta:
model = Foo
class FooAdmin(admin.ModelAdmin):
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
Once you've done this, the <select> dropdown will order the user objects according to username. No need to worry about to other fields on Foo... you only need to specify the overrides in your FooAdminForm class. Unfortunately, you'll need to provide this custom form definition for every model having an FK to User that you wish to present in the admin site.
Jarret's answer above should actually read:
from django.contrib.auth.models import User
from django.contrib import admin
from django import forms
from yourapp.models import Foo
class FooAdminForm(forms.ModelForm):
class Meta:
model = Foo
def __init__(self, *args, **kwds):
super(FooAdminForm, self).__init__(*args, **kwds)
self.fields['user'].queryset = User.objects.order_by(...)
class FooAdmin(admin.ModelAdmin):
# other stuff here
form = FooAdminForm
admin.site.register(Foo, FooAdmin)
so the queryset gets re-evaluated each time you create the form, as opposed to once, when the module containing the form is imported.