I'm trying to add some extra attributes to a user by way of a User Profile in Django (1.2.5, the version supplied in Ubuntu natty), but whenever I create a new user through the admin console, with one of the new attributes included (eg, 'phone'), I get a "column user_id is not unique" IntegrityError.
I see that other people have had this problem, and they seem to have solved it by adding a unique dispatch_uid to the userprofile creation signal, but that's not working for me:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
class UserProfile(models.Model):
user = models.OneToOneField(User)
phone = models.CharField(max_length=40,blank=True,null=True)
def create_user_profile(sender, instance, **kwargs):
UserProfile.objects.get_or_create(user=instance)
post_save.connect(create_user_profile, sender=User, dispatch_uid="users-profilecreation-signal")
AUTH_PROFILE_MODULE is set to point at this class, in settings.py.
Anyone know what I'm doing wrong?
There are two attempts to create the same profile. One from the signal and one directly from the inlined admin form.
This question/answer covers it and a potential solution in more detail.
Perhaps check to see if this is a signal for creating a user. You may be seeing a race condition with a number of callbacks.
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.get_or_create(user=instance)
Related
I am using the standard django.contrib.auth.models User model and have extended it to create a Profile model with a one to one relationship with User to hold the profile picture. The signal for this is working fine. I since added another model, Roles, which needs a regular foreign key to User as a user can have multiple roles. The latter gives consistent errors no matter how I try to configure it including giving the fields different related_name in case it was confused which was which as well as having the Role model with a relationship to Profile rather than User but no matter how I seem to tackle it, I can't get the signal to work.
relevant models.py file code is as follows:
models.py:
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
...
class Role(TimeStampedModel):
user = models.ForeignKey(User, on_delete=models.CASCADE)
role = models.IntegerField('Roles',choices=Roles.choices, default=0)
...
signals.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from core.models import Profile, Role
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
Role.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
instance.role.save()
The debug error I am getting is as follows:
AttributeError at /login/
'User' object has no attribute 'role'
Request Method: POST
Request URL: http://127.0.0.1:8000/login/
Django Version: 3.0.8
Exception Type: AttributeError
Exception Value:
'User' object has no attribute 'role'
Exception Location: C:\project\path\core\signals.py in save_profile, line 15
I expect it's something to do with setting up a separate signal rather than having them in the same def but haven't been able to crack it after trying numerous ways. Likely just a silly thing I'm missing and will be grateful for a nudge in the right direction.
Thanks for taking a look.
Simon
user have not role but roles, is the default related name in foreignkey
you can change this by foreignkey(... , related_name="your_name")
so you can't do this for 2 reason
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
instance.role.save()
instance not have role but roles
instance.roles is not object but a queryset/list of role models
I use the built-in User model in django, but i want use some custom fields, i was create new model
class Profile(models.Model):
user = models.OneToOneField(User)
profile_img = models.ImageField(null=True)
def __str__(self):
return self.user.username
this is my custom model.
But when i create new user it doesn't display in admin(in profile table), i need to add user from admin panel, after adding it works fine. what i should to do to display Profile info of all users?
p.s. When i was create table profile and was try to select info from joined tabels, sql query wasn't return anything, but after adding existed user to Profile table sql query return all what i want
To create a new Profile object when a new user is created, you can use a pre_save receiver:
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(
user=instance
)
The created parameter is to check if the object has been created ( created=True ) or updated ( created=False )
I am not sure to understand what you want... If you want to manage profile in admin app you have to register it.
from django.contrib import admin
from myproject.myapp.models import Profile
class ProfileAdmin(admin.ModelAdmin):
pass
admin.site.register(Profile, ProfileAdmin)
Edit: you can use a signal to automatically create a profile when creating a user.
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from myproject.myapp.models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance **kwargs):
Profile.objects.create(user=instance)
Note that sometime using signals may be hard to maintain. You can also use AbstractBaseUser. This is a classical issue, which is widely covered in a lot of posts.
One I particularly like: https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html
I've been searching around trying to figure this out, I want to add simple things to my Profile model like avatar, contact info, etc. However when following tutorials that I found online on how to do this, I am only met with errors.
This is what my model looks like (tracks/models.py):
from django.db import models
from django.core.exceptions import ValidationError
from django.core.files.images import get_image_dimensions
from django.contrib.auth.models import User
...
class Profile(models.Model):
def generate_user_folder_avatar(instance, filename):
return "uploads/users/%s/%s.png" % (instance.user, 'avatar')
user = models.OneToOneField(User)
avatar = models.ImageField(upload_to=generate_user_folder_avatar,validators=[is_square_png])
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
I've set the AUTH_PROFILE_MODULE = 'tracks.Profile' in settings.py but when I run my server I get this error:
NameError: name 'post_save' is not defined
Any idea what I am doing wrong here? I'm using Django 1.9 and Python 3
NameError: name 'post_save' is not defined
you should do the import:
from django.db.models.signals import post_save
note #1: you may be interested in the fact that Django provides more explicit and clear way to extend User model:
https://docs.djangoproject.com/en/1.9/topics/auth/customizing/#extending-django-s-default-user
note #2: you probably want to connect signals not somewhere in models, but like that: https://stackoverflow.com/a/21612050/699864
I have some issue.
I import django User model, create new user then trying to get it profile, all what I have is "Profile matching query does not exist". Why? I just create the user.
Here is my code:
from django.contrib.auth.models import User
user = User.objects.create(username="stackoverflow", password="tester1234")
user.get_profile()
You might have forgotten to set
AUTH_PROFILE_MODULE
in your settings.py.
The online documentation suggests that get_profile()...
Returns a site-specific profile for this user. Raises django.contrib.auth.models.SiteProfileNotAvailable if the current site doesn't allow profiles, or django.core.exceptions.ObjectDoesNotExist if the user does not have a profile. For information on how to define a site-specific user profile, see the section on storing additional user information below.
Are you sure you've enabled profiles?
From your code snippet it looks like you've perhaps not created a profile which is a separate class (see here.
also write save method in signals.py:
#receiver(post_save,sender=User)
def save_profile(sender,instance,**kwargs):
instance.profile.save()
and also add this in app.py
class UserProfileConfig(AppConfig):
name = 'UserProfile'
def ready(self):
import UserProfile.signals
Django docs define this clearly, I miss that, sorry
Storing additional information about users
If you'd like to store additional information related to your users,
Django provides a method to specify a site-specific related model --
termed a "user profile" -- for this purpose.
To make use of this feature, define a model with fields for the
additional information you'd like to store, or additional methods
you'd like to have available, and also add a OneToOneField named user
from your model to the User model. This will ensure only one instance
of your model can be created for each User. For example:
from django.contrib.auth.models import User
class UserProfile(models.Model):
# This field is required.
user = models.OneToOneField(User)
# Other fields here
accepted_eula = models.BooleanField()
favorite_animal = models.CharField(max_length=20, default="Dragons.")
To indicate that this model is the user profile model for a given
site, fill in the setting AUTH_PROFILE_MODULE with a string consisting
of the following items, separated by a dot:
The name of the application (case sensitive) in which the user profile model is defined (in other words, the name which was passed to
manage.py startapp to create the application).
The name of the model (not case sensitive) class.
For example, if the profile model was a class named UserProfile and
was defined inside an application named accounts, the appropriate
setting would be:
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
When a user profile model has been defined and specified in this
manner, each User object will have a method -- get_profile() -- which
returns the instance of the user profile model associated with that
User.
The method get_profile() does not create a profile if one does not
exist. You need to register a handler for the User model's
django.db.models.signals.post_save signal and, in the handler, if
created is True, create the associated user profile:
in models.py
from django.contrib.auth.models import User from
django.db.models.signals import post_save
# definition of UserProfile from above
# ...
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
in django by default when syncdb is run with django.contrib.auth installed, it creates default permissions on each model... like foo.can_change , foo.can_delete and foo.can_add. To add custom permissions to models one can add class Meta: under the model and define permissions there, as explained here https://docs.djangoproject.com/en/4.1/topics/auth/customizing/#custom-permissions
My question is that what should I do if I want to add a custom permission to the User model? like foo.can_view. I could do this with the following snippet,
ct = ContentType.objects.get(app_label='auth', model='user')
perm = Permission.objects.create(codename='can_view', name='Can View Users',
content_type=ct)
perm.save()
But I want something that plays nicely with syncdb, for example the class Meta under my custom models. Should I just have these in class Meta: under UserProfile since that is the way to extend the user model. but is that the RIGHT way to do it? Wouldn't that tie it to UserProfile model?
You could do something like this:
in the __init__.py of your Django app add:
from django.db.models.signals import post_syncdb
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
# custom user related permissions
def add_user_permissions(sender, **kwargs):
ct = ContentType.objects.get(app_label='auth', model='user')
perm, created = Permission.objects.get_or_create(codename='can_view', name='Can View Users', content_type=ct)
post_syncdb.connect(add_user_permissions, sender=auth_models)
I don't think there is a "right" answer here, but i used the exact same code as you except i changed Permission.objects.create to Permission.objects.get_or_create and that worked find to sync with syncdb
An updated answer for Django 1.8. The signal pre_migrate is used instead of pre_syncdb, since syncdb is deprecated and the docs recommend using pre_migrate instead of post_migrate if the signal will alter the database. Also, #receiver is used to connect add_user_permissions to the signal.
from django.db.models.signals import pre_migrate
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth import models as auth_models
from django.contrib.auth.models import Permission
from django.conf import settings
from django.dispatch import receiver
# custom user related permissions
#receiver(pre_migrate, sender=auth_models)
def add_user_permissions(sender, **kwargs):
content_type = ContentType.objects.get_for_model(settings.AUTH_USER_MODEL)
Permission.objects.get_or_create(codename='view_user', name='View user', content_type=content_type)
This is a bit hacky but mentioning it here anyway for reference.
My site has a generic model called Setting, which stores various settings concerning the site I want certain users to be able to edit, without needing to go through me the developer (like registration limit, or an address, or the cost of items, etc).
All the permissions that don't nicely map onto other models (eg "Send Password Reminder Email to Student", "Generate Payment Reconciliation Report", "Generate PDF Receipt"), which really just relate to pages that get viewed in the admin area, get dumped onto this Setting model.
For example, here's the model:
class Setting(models.Model):
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(editable=False)
description = models.TextField()
value = models.TextField()
class Meta:
#for permissions that don't really relate to a particular model, and I don't want to programmatically create them.
permissions = (
("password_reminder", "Send Password Reminder"),
("generate_payment_reconciliation_report", "Generate Payment Reconciliation Report"),
("generate_pdf_receipt", "Generate PDF Receipt"),
)
Do each of those settings strictly relate to the Setting model? No, which is why I said this is a bit hacky. But it is nice that I can now just dump all those permissions here, and Django's migration commands will take care of the rest.