I want to create an auto-generated membership id of a user in the profile table based on the current date and username. User table has OneToOneField relationship with the profile table. So when I create a user, I have to put username in the registration form. The signals.py creates a profile row in the table for the user. I want when the profile is created it would have a membership id which is the mix of current date and username. My code is as follow:
singlas.py
from django.db.models.signals import post_save, pre_save
from .models import Ext_User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=Ext_User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
models.py
class Profile(models.Model):
user = models.OneToOneField(Ext_User, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics', null=True, blank=False)
membership_id = models.CharField(max_length=50, default='', null=True, blank=True)
def __str__(self):
return f'{self.user.username} Profile'
I have got some guideline to user pre_save into signals.py but don't understand how to figure out.
You can try this
#receiver(post_save, sender=Ext_User)
def create_profile(sender, instance, created, **kwargs):
if created:
profile = Profile()
profile.membership_id = str(instance.username) + str(datetime.datetime.now())
profile.user_id = instance.pk
profile.save()
format DateTime as your desired format
Related
I'm trying to add one-to-one field to the default Django user model but
for some reason I keep getting error from the database:
django.db.utils.IntegrityError: NOT NULL constraint failed: frontend_usermodel.test_suites_id
this is models file:
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.db import models
class TestSuite(models.Model):
id = models.IntegerField(primary_key=True)
...
...
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class UserModel(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
test_suites = models.OneToOneField(TestSuite, on_delete=models.CASCADE)
def __str__(self):
return self.email
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserModel.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
What can I do to solve this ?
UPDATE:
class TestSuite(models.Model):
id = models.IntegerField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
...
...
Your UserModel requires both fields user and test_suites, so this line:
UserModel.objects.create(user=instance)
will fail because test_suites is None and therefore the NULL constraint is violated. You have to pass also a TestSuite instance to create your UserModel instance.
Although I don't know the exact business requirements of your application, it would seem more logical to me to set the OneToOneField on the TestSuite. I can imagine a user without test suite, and I would expect only when you create a test suite, you would assign the user. By doing that, you won't need a TestSuite instance when creating a User.
I am trying get a picture link from a facebook account but get this message:
django.db.utils.IntegrityError: UNIQUE constraint failed:
user_profile.user_id
I can see a picture link in console, but I cannot save it in user profile.
here is my model.py when I'm trying to do that.
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
from allauth.account.signals import user_signed_up, user_logged_in
from allauth.socialaccount.models import SocialAccount
import hashlib
try:
from django.utils.encoding import force_text
except ImportError:
from django.utils.encoding import force_unicode as force_text
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,
related_name='userprofile')
city = models.CharField(max_length=30, blank=True)
about = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', verbose_name='Images',
blank=True)
sound = models.BooleanField(default=False)
points = models.DecimalField(max_digits=4, decimal_places=2, default=0.00)
energy = models.IntegerField(default=0)
avatar_url = models.URLField(max_length=500, blank=True, null=True)
class Meta:
db_table = 'user_profile'
verbose_name = 'Profile'
verbose_name_plural = 'Profiles'
def __str__(self):
return str(self.user)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
##receiver(user_logged_in)
#receiver(user_signed_up)
def set_initial_user_names(request, user, sociallogin=None, **kwargs):
preferred_avatar_size_pixels = 25
if sociallogin:
if sociallogin.account.provider == 'facebook':
picture_url = "http://graph.facebook.com/{0}/picture?width={1}&height={1}".format(
sociallogin.account.uid, preferred_avatar_size_pixels)
profile = UserProfile(user=user, avatar_url=picture_url)
#profile = UserProfile.objects.get(user=user)
#profile.avatar_url = picture_url
profile.save()
If I am doing like that at the end:
#profile = UserProfile(user=user, avatar_url=picture_url)
profile = UserProfile.objects.get(user=user)
profile.avatar_url = picture_url
profile.save()
I am not gettin any message in the console, but user profile doesn't save.
This line profile = UserProfile(user=user, avatar_url=picture_url) is causing the problem as you are trying to create a new instance of profile which already exists. The profile becomes unique because of OneToOne field in your UserProfile model.
And you don't need to get the user from the database because set_initial_user_names function is already passing the registered user to you as a parameter. So just do user.userprofile. Then you can just update the user with new information.
Also I would suggest you to download the picture from the url provided and then save it in your image field of your model like this:
import urllib
from django.core.files import File
# for python 2: result = urllib.urlretrieve(picture_url)[0]
result = urllib.request.urlretrieve(picture_url)[0] # for python 3
user.userprofile.avatar.save('test.jpg', File(open(result, 'rb')))
user.userprofile.save()
I have the following in model.py:
from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
university = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
ROLE = (
('CUSTOMER', 'User'), # (value to be set on model, human readable value)
('WORKER', 'Worker'),
)
role = models.CharField(max_length = 20, choices = ROLE, default = 'USER')
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I also have a corresponding form that, when filled out and sumbitted, saves to the database as a Profile properly.
What I do not understand is instance.profile.save() how does this work? To me it appears it should be instance.Profile.save() since, Profileexists. I am not sure where this lowercase profileis coming from?
In Django, reverse accessors are defined with the name of the related model in lowercase by default.
As your Profile model has a one-to-one relationship with User model, you can access profile instance belonging to a User instance (say user) as user.profile.
You can override this naming with a parameter where you define the OneToOneField in your model definition with the keyword argument related_name.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='myprofile')
Now, you access via user.myprofile
See Django documentation here
By the way, be careful when you are accessing profile of a user via User instance. If there is no Profile record associated with that User instance, a DoesNotExist exception is raised.
I have this model
from django.contrib.auth.models import User
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from my_app.models import Teams
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams, on_delete=models.CASCADE)
class Meta:
app_label = 'my_app'
def __str__(self):
return self.name
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
I then go into the shell
from django.contrib.auth.models import User
when I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test')
I unsurprisingly get
IntegrityError: NOT NULL constraint failed: my_app_profile.team_id
but when I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test', team='developer')
I get
TypeError: 'team' is an invalid keyword argument for this function
If I type
user = User.objects.create_user(username='test', email='dummyemail#dum.com', password='test', profile.team='developer')
I get
SyntaxError: keyword can't be an expression
Any help, hints or guidance would be greatly appreciated
You haven't allowed team to be null, so when you create a profile like so:
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
# Team isn't defined
Profile.objects.create(user=instance)
You will get an error.
Allow team to be null (or set a default in the code above):
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams, on_delete=models.CASCADE, null=True)
class Meta:
app_label = 'my_app'
def __str__(self):
return self.name
When you run
User.objects.create_user(username='test', email='dummyemail#dum.com', password='test')
you get IntegrityError because of post_save signal. It tries to create a Profile instance but in Profile model team cannot be NULL. By default, model fields have null=False.
When you run
user = User.objects.create_user(username='test', email='dummyemail#dum.com',
password='test', team='developer')
you get error because team is a field in Profile model, not User model.
When you run
user = User.objects.create_user(username='test', email='dummyemail#dum.com',
password='test', profile.team='developer')
you get error because you cannot use . to refer attributes. You need to use __ to filter on foreign key properties. See this question for example. However, even if you use __, it will still give error because their is no field named profile in User model.
One more thing I would suggest is to combine both post_save signals into one because both have same sender.
See the documentation about ForeignKey.related_name. By default, OneToOneField have related name to join one model to an ohter, but ForeignKey don't. Just have this option and you can do user.team to access to the model related.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
team = models.ForeignKey(Teams,
on_delete=models.CASCADE,
related_name='team',
null=True)
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)