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
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
Well.. I started to create simple app. Following official doc of Django, I created auth logic in separate app with name users, like this:
users/urls.py:
from django.urls import path, re_path, include
from . import views
urlpatterns = [
path('', include('django.contrib.auth.urls')),
path('profile/', views.redirect_to_user_profile, name='redirect-user-profile'),
re_path('profile/(?P<pk>\d+)/', views.UserProfileView.as_view(), name='user-profile'),
path('register/', views.UserRegisterView.as_view(), name='user-register'),
users/views.py:
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.views import generic
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
# Create your views here.
def redirect_to_user_profile(request):
url = f'/users/profile/{request.user.id}'
return HttpResponseRedirect(redirect_to=url)
class UserProfileView(generic.DetailView):
model = User
template_name = 'user_profile.html'
class UserRegisterView(generic.CreateView):
form_class = UserCreationForm
template_name = 'register.html'
success_url = '/users/login'
Everything was fine, so I decided to extent basic Django user, to add profile image for example (and more fields later) like this:
users/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
# Create your models here.
class ProfileUser(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_image = models.URLField()
#receiver(post_save, sender=User) # Still don't know how, but next rows create ProfileUser when User is created
def create_user_profile(sender, instance, created, **kwargs):
if created:
ProfileUser.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profileuser.save()
def __str__(self):
return f"{self.user}"
Still working fine. Then I decided to add FB login, after some researh I found django-allauth module, following it documentation and add one row in users/urls.py:
path('fb/', include('allauth.urls')),
It also working, But here comes the problem currently I have two separate modules for authorization:
Basic django User auth logic:
And django-allauth auth logic:
How to handle this (best way):
What I think: To use only Django-allauth, but is there a way to extend it with additional ProfileUser, like extend it now with Django User?
Using only Django-allauth is a good assumption.
The way to extend Django User is by something like that:
class ProfileUser(AbstractUser):
profile_image = models.URLField()
REQUIRED_FIELDS = ['email']
class Meta:
managed = True
db_table = 'profile_user'
However, change like that could enforce to make migrations from the beginning. Or trying to fix them manually. This is because of auth app migrations are done as a second just after contenttypes.
Okay, based on #artusiep answer and a lot of research, I found that the best way is to use one of them, but not both at the same time. Also found very good tutorial about both implementation here
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 am trying to create profile system where my users django username will be their username on the site, and also allow them to create a bio. However when I try to migrate, I get this error:
AttributeError: 'OneToOneField' object has no attribute 'model'
Here is my models.py files:
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
bio = models.TextField(null=True)
slug = models.SlugField(default=user)
def __unicode__(self):
return "%s's profile" % self.user
def create_profile(sender, instance, created, **kwargs):
if created:
profile, created = UserProfile.objects.get_or_create(user=instance)
# Signal while saving user
from django.db.models.signals import post_save
post_save.connect(create_profile, sender=User)
And here is my admin.py:
from django.contrib import admin
from profiles.models import UserProfile
class UserProfileAdmin(admin.ModelAdmin):
list_display = ["user"]
admin.site.register(UserProfile, UserProfileAdmin)
Anyone know what the problem is? Thanks.
Try without importing the User model from django.contrib.auth.
Remove (or comment out) the User import line, the signal binding, and switch to string based model specification of relation in OneToOneField. The example of string based relation specification can be found in the docs.
This looks like a circular dependency thing.
If you'd still get an error, try to remove stuff you don't need from INSTALLED_APPS.
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)