I'm following the 'User profile' approach to extend my User model, like so:
# models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE, primary_key=True)
my_field = models.CharField(max_length=100)
# signals.py
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
With this approach, I have to explicitly call user.profile.save(), which to me feels clunky, as I want the profile to give the illusion it is part of the User object:
# views.py
def some_func(request):
user = User.objects.create_user('dummy', 'dummy#dummy.com', '12345678')
user.profile.my_field = 'hello'
user.save() # This does not persist the profile object...
user.profile.save() # ...this does
To remedy this, I've changed create_user_profile() to the following, which works:
# signals.py
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
profile = UserProfile.objects.get_or_create(user=instance)
profile.save()
Numerous examples I've encountered do not use this approach. Are there any caveats to using this approach?
The better way is to specify a custom user model.
from django.contrib.auth.models import AbstractUser
from django.db import models
class User(AbstractUser):
custom_field = models.ForeignKey(
'contracts.Contract'
)
...
class Meta(AbstractUser.Meta):
swappable = 'AUTH_USER_MODEL'
You have to update the settings.py defining the AUTH_USER_MODEL property:
AUTH_USER_MODEL = 'app_name.User'
You can use a custom User model like this :
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return f'{self.user.username} Profile'
and then the signals.py file :
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_profile(sender, instance, **kwargs):
instance.profile.save()
what the signals.py file does here is to inform you when a new Profile object is created of the User type which you can further use to create forms that update/create the user's profile.
And to make all this work, you need to import the signals.py file in the apps.py file of your app. For example, this is what your apps.py file would look like :
from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
import users.signals
Yes, there are a few. In the following situations the post_save signal would not be fired.
1 If the save method does not successfully save the object (such as when an IntegrityError occurs)
2 When you call MyModel.objects.update()
3 When you override the save method and forget to call the superclass method.
4 When your signal receiver hasn't been successfully registered.
In these situations your profile wouldn't be saved.
Related
folder structure
enter image description here
models.py from face_detect
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
class Profile(models.Model):
user = models.ForeignKey(User,on_delete=models.CASCADE,null=True)
mobile = models.CharField(max_length=10,null=True)
add = models.CharField(max_length=10,null=True)
image = models.FileField(null=True)
def __str__(self):
return self.user.username
models.py from otp
from django.db import models
from django.contrib.auth.models import User
from face_detect.models import Profile
import random
# Create your models here.
class Code(models.Model):
number = models.CharField(max_length=5, blank=True)
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return str(self.number)
#otp verification
def save(self, *args, **kwargs):
number_list = [x for x in range(10)]
code_items = []
for i in range(5):
num = random.choice(number_list)
code_items.append(num)
code_string = "".join(str(item) for item in code_items)
self.mobile = code_string
super().save(*args,**kwargs)
signals.py from otp
from face_detect.models import Profile
from django.contrib.auth.models import User
from .models import Code
from django.db.models.signals import post_save
from django.dispatch import receiver
#receiver(post_save, sender=User)
def post_save_generate_code(sender, instance, created, *args, **kwargs):
if created:
Code.objects.create(user=instance)
apps.py from otp
from django.apps import AppConfig
class OtpConfig(AppConfig):
name = 'otp'
def ready(self):
import otp.signals
init.py from otp
default_app_config = 'otp.apps.OtpConfig'
admin.py from otp
from django.contrib import admin
from .models import Code
# Register your models here.
admin.site.register(Code)
code seen in http://127.0.0.1:8000/admin/otp/code/
enter image description here
code is not visible.
how can i make the code visible so i can use it for otp verification while login.
Thanks in advance
In admin page you will see the __str__ method, so there seems to be nothing wrong with your code most probably your code instance's value is "", Also one other thing
def __str__(self):
return str(self.number)
you dont need to cast it to str here its already char field
and another thing is here:
def __str__(self):
return self.user.username
in Profile model you made user nullable so this means not all the profile instances might not have user so you will get NoneType has no attribute username
change it to this to prevent this errors:
def __str__(self):
return self.user.username if self.user else ""
I created the custom Subscriber one-to-one model to extend the existing Django's User model. (I know that it's better to use an AbstractUser, but I just wanted to try this out for the sake of learning)
I can create a new User successfully through the shell, and I can edit (in the admin panel) the custom fields that User has after the creation. However, I can't create new users in the admin panel without the usage of shell, because of an error.
I suppose there is something wrong with admin.py?
Error:
IntegrityError at /admin/auth/user/add/
UNIQUE constraint failed: app_subscriber.user_id
models.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 Subscriber(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
location = models.CharField(max_length=30, blank=True)
#receiver(post_save, sender=User)
def create_user_subscriber(sender, instance, created, **kwargs):
if created:
Subscriber.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_subscriber(sender, instance, **kwargs):
instance.subscriber.save()
admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.models import User
from . import models
# Define an inline admin descriptor for Subscriber model
# which acts a bit like a singleton
class SubscriberInline(admin.StackedInline):
model = models.Subscriber
can_delete = False
verbose_name_plural = 'subscribers'
# Define a new User admin
class UserAdmin(BaseUserAdmin):
inlines = (SubscriberInline,)
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)
is not good to create tow same signal ( post_save for User), create only one and do all things on it
#receiver(post_save, sender=User)
def create_user_subscriber(sender, instance, created, **kwargs):
if created:
Subscriber.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_subscriber(sender, instance, **kwargs):
instance.subscriber.save()
Your error tell you can't create 2 subriber with same user ( UNIQUE constraint )
this slove your probleme
#receiver(post_save, sender=User)
def create_user_subscriber(sender, instance, created, **kwargs):
# you check if instance are 'subscriber' related name create by OneToOneField , if not create Subcriber model
if created and not hasattr(instance, 'subscriber'):
Subscriber.objects.create(user=instance)
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'm trying to extend the built-in user and add some more information to it. I have two apps in my django project- general and user_details. Inside my user_details app, in the models.py file, I have the following...
from django.db import models
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from general.models import Ward
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
firstname = models.CharField(max_length=20)
middlename = models.CharField(max_length=20)
lastname = models.CharField(max_length=20)
loc_id = models.CharField(max_length=8, unique=True)
ward = models.ForeignKey(Ward, related_name="wards", on_delete=models.CASCADE)
phone = models.CharField(max_length=10, unique=True)
address = models.CharField(max_length=255)
postal_code = models.CharField(max_length=15)
def __str__(self):
return "{} {}".format(self.user.first_name, self.user.last_name)
class Meta:
verbose_name_plural = "User details"
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
else:
instance.profile.save()
I have successfully done this before but today for some reason when I try to log in via the django admin, I keep getting the RelatedObjectDoesNotExist at /admin/login/, no such column: user_details_profile.id. There are no other components involved, no views or any of that. It's just models and the admin panel. I'm trying to get the model to show in the admin panel but I ran into this error every time I try to log in.
Here is the traceback http://dpaste.com/0W7653C.
Can anyone tell me what I am doing wrong please and how I can get it to work. Help.
If you define a OneToOneField that does not mean that every object of the targeted model contains an object from the referencing model. A OneToOneField in essence is a ForeignKey with a unique=True (and some extra logic to do proper naming).
Here it thus means that although every Profile has a related user, not every user has a Profile. It is possible that there are Users for which no Profile exists.
If you thus query some_user.profile there are two scenario's that can unfold:
there is a related Profile object that is the fetched, and returned; or
there is no such object, and then it raises a RelatedObjectDoesNotExist error.
There have been some proposals to return None in the latter case, but due to backwards compatibility, this will probably not be implemented in the (near) future, or implemented at all.
So you probably have a user for which there is no profile. Based on the full traceback, the error happens on the line instance.profile.save(). We can fix this by creating a profile in case there is no such profile:
from django.core.exceptions import ObjectDoesNotExist
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
try:
instance.profile.save()
except ObjectDoesNotExist:
Profile.objects.create(user=instance)
We thus check if the instance has a .profile that leads to a Profile object. In case it has not, it will raise an exception, and then we create one.
This is also more in the Easier to Ask Forgiveness than Permission (EAFP) spirit of Django.
Use this. It will work
from django.core.exceptions import ObjectDoesNotExist
#receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
try:
instance.profile.save()
except ObjectDoesNotExist:
Profile.objects.create(user=instance)
Just in case someone else might face this same problem: Try creating a python file in the app of your project that's implementing the registration or sign up and call it "signals.py" and add the following code snippet to it. It worked for me perfectly.
from django.db.models.signals import post_save
from django.contrib.auth.models import User
from django.dispatch import receiver
from .models import Profile
#receiver(post_save, sender = User)
def create_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user = instance
def save_profile(sender, instance, **kwargs):
instance.profile.save()
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)