I have a problem using Djangos Signals while creating a User and a Profile.
I'm trying to create a Profile upon creating a User, but I keep getting the error:
AttributeError at /user/create/
'User' object has no attribute 'profile'
So here is my User Model:
from django.db import models
from django.contrib.auth.models import AbstractUser
from django_countries.fields import CountryField
class User(AbstractUser):
"""auth/login-related fields"""
is_a = models.BooleanField('a status', default=False)
is_o = models.BooleanField('o status', default=False)
def _str_(self):
return "{} {}".format(self.first_name, self.last_name)
and here is my Profile Model:
from django.db import models
from django_countries.fields import CountryField
from django.contrib.auth import get_user_model
User = get_user_model()
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
"""non-auth-related/cosmetic fields"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='Profile')
birth_date = models.DateTimeField(auto_now=False, auto_now_add=False, null=True)
nationality = CountryField(null=True)
GENDER_CHOICES = (
('M', 'Male'),
('F', 'Female'),
)
gender = models.CharField(max_length=1, choices=GENDER_CHOICES, null=True)
def __str__(self):
return f'{self.user.username} Profile'
My User Serializer:
from rest_framework import serializers
from django.contrib.auth import get_user_model
from ..models.model_user import *
class UserIndexSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'id',
'username',
'password',
'first_name',
'last_name',
'email',
'is_a',
'is_o'
]
class UserCreateSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = [
'username',
'password',
'first_name',
'last_name',
'email',
'is_a',
'is_o'
]
extra_kwargs = {'password': {'write_only': True}}
def create(self, validated_data):
user = User(
username=validated_data['username'],
password=validated_data['password'],
first_name=validated_data['first_name'],
last_name=validated_data['last_name'],
email=validated_data['email'],
is_a=validated_data['is_a'],
is_o=validated_data['is_o']
)
user.set_password(validated_data['password'])
user.save()
return user
class UserDetailsSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = '__all__'
My signals.py:
from django.db.models.signals import post_save
from django.contrib.auth import get_user_model
User = get_user_model()
from django.dispatch import receiver
from .models.model_profile import *
"""receivers to add a Profile for newly created users"""
#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()
And when I'm using Postman to post a User:
{
"username":"16",
"password":"12345678",
"first_name":"Al",
"last_name":"Pongvf",
"email":"ahgj#live.fr",
"is_a":"False",
"is_o":"False"
}
It gives me this error message:
AttributeError at /user/create/
'User' object has no attribute 'profile'
I've searched for a solution, but I didn't get lucky:
StackOverflow
1
StackOverflow
2
Medium
Does anyone know what am I missing? or doing wrong?
Thanks!
Your related_name="Profile" on the Proflile model is Profile with a capital. You need to reference it with a capital to use it. I would recommend you rename it to lowercase and make new migrations.
For example:
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.Profile.save()
But really you should change this:
class Profile(models.Model):
"""non-auth-related/cosmetic fields"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
Related
When I try to edit a user (using a custom UserChangeForm) in the Django admin panel, validation insists that fields I have set blank=True in the model are required.
I don't know where to begin solving this; I had the same issue with the CustomUserCreationForm but reverted to using the default which works as expected (asks for username, password1 & password2, creates the user with blank display_name, bio and profile_picture fields).
models.py:
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
display_name = models.CharField(max_length=30, blank=True, null=True)
bio = models.TextField(blank=True, null=True)
profile_picture = models.ImageField(upload_to='images/', blank=True, null=True)
def save(self, *args, **kwargs):
if not self.display_name:
self.display_name = self.username
super().save(*args, **kwargs)
def __str__(self):
return self.username
forms.py:
from django import forms
from django.contrib.auth.forms import UserChangeForm
from .models import CustomUser
class CustomUserChangeForm(UserChangeForm):
display_name = forms.CharField(label="display_name")
bio = forms.CharField(widget=forms.Textarea)
profile_picture = forms.ImageField(label="profile_picture")
class Meta():
model = CustomUser
fields = ("username", "email", "display_name", "bio", "profile_picture")
admin.py:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .forms import CustomUserChangeForm
from .models import CustomUser
class CustomUserAdmin(UserAdmin):
form = CustomUserChangeForm
fieldsets = (
(None,
{'fields': ('username', 'password', 'email', 'display_name', 'bio', 'profile_picture')}
),
)
model = CustomUser
list_display = ["username", "email",]
admin.site.register(CustomUser, CustomUserAdmin)
From the Django documentation:
By default, each Field class assumes the value is required, so if you
pass an empty value – either None or the empty string ("") – then
clean() will raise a ValidationError exception:
So you have to add required=False in your forms.py. For example:
display_name = forms.CharField(required=False, label="display_name")
Hi i working with Django .
I'm trying to turn my user into a profile with signals
When registering the user through a form
I get the following error :
TypeError at /Registro/ Profile() got an unexpected keyword argument 'user' and
the user is created in 'AUTHENTICATION AND AUTHORIZATION' (ADMIN), but not in profiles.
Models.py
from django.db import models
class Profile(models.Model):
id = models.AutoField(primary_key=True)
nombreUsuario = models.CharField('Nombre usuario : ', max_length=15, null = False, blank=False, unique=True)
email = models.EmailField('Email', null=False, blank=False, unique=True)
password = models.CharField('Contraseña', max_length=25, null=False, blank=False, default='')
#Unique sirve para validar si el usuario existe y sea unico el email y nombre de usuario.
nombres = models.CharField('Nombres', max_length=255, null= True, blank=True)
apellidos = models.CharField('Apellidos', max_length=255, null=True, blank=True)
imagen = models.ImageField(upload_to='img_perfil/',default='batman.png',null=True, blank=True)
fecha_union = models.DateField('Fecha de alta', auto_now = False, auto_now_add = True)
facebook = models.URLField('Facebook', null=True, blank=True)
instagram = models.URLField('Instagram', null=True, blank=True)
def __str__(self):
return f'Perfil de {self.nombreUsuario}'
class Meta:
verbose_name = "Perfil"
verbose_name_plural = "Perfiles"
views.py
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect
from django.views.generic.edit import FormView
from .models import Profile
from .forms import RegistrationForm
from django.contrib import messages
from django.contrib.auth.models import Group
from django.utils.decorators import method_decorator
def iniciarSesion(request):
return render(request,'social/inicio.html')
def registro(request):
if request.method == 'POST':
fm = RegistrationForm(request.POST)
if fm.is_valid():
user=fm.save()
username = fm.cleaned_data.get('username')
messages.success(request,'Registration Created Successfully')
redirect('feed')
else:
fm = RegistrationForm()
return render(request, 'social/registrarse.html',{'fm':fm})
def feed(request):
return render(request,'social/feed.html')
def profile(request):
return render(request,'social/profile.html')
forms.py
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class RegistrationForm(UserCreationForm):
class Meta:
model=User
fields=[
'username',
'email',
'first_name',
'last_name',
]
signals.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.contrib.auth.models import Group
from .models import Profile
def create_user_profile(sender, instance, created, **kwargs):
if created:
#group = Group.objects.get(name = 'profile')
#instance.groups.add(group)
Profile.objects.create(
user = instance,
name= instance.username,
)
Profile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
I need help with this code!
Well, what exactly did you expect? Your Profile model doesn't seem to have a fk to user and that's what you are trying to do (twice) in the signal.
Just add user fk to User model and create it once in the signal.
How can i add extended Profile model fields (fields which are not available in custom user model fields) into custom users admin users.admin?
what i am trying to do is that i want too see Profile model fields like photo, date_of_birth, country, phone etc.. inside the Personal Info(see in image) & i can make changes in it from here.
profile model
from django.db import models
from django.dispatch import receiver
from django.urls import reverse
from django.db.models.signals import post_save
from django.contrib.auth import get_user_model # or from users.models import User
User = get_user_model()
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
photo = models.ImageField(null=True, blank=True)
date_of_birth = models.DateField(null=True, blank=True)
phone = models.IntegerField(null=True, blank=True)
country = models.CharField(max_length=150, null=True, blank=True)
city = models.CharField(max_length=150, null=True, blank=True)
bio = models.TextField(max_length=150, null=True, blank=True)
def __str__(self):
return str(self.user.email)
def get_absolute_url(self):
return reverse('profiles:profile-detail', kwargs={'pk':self.pk})
def post_save_user_model_receiver(sender, instance, created, *args, **kwargs ):
# when a user is created(custom user model)like signup or through admin it will create those user's profile too
if created:
try:
Profile.objects.create(user=instance) # it create those user's profile
except:
pass
post_save.connect(post_save_user_model_receiver, sender=User)
users.admin
from django.contrib import admin
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth import get_user_model # or from .models import User
from .forms import UserAdminCreationForm, UserAdminChangeForm
# Register your models here.
User = get_user_model() # or from .models import User
class UserAdmin(BaseUserAdmin):
# The forms to add and change user instances
form = UserAdminChangeForm
add_form = UserAdminCreationForm
# The fields to be used in displaying the User model.
# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ('email', 'first_name', 'get_phone', 'last_login', 'date_joined', 'is_admin')
list_filter = ('admin', 'staff', 'active')
list_select_related = ('profile',)
def get_phone(self, instance): # to show the Phone in list display from the Profile Model
return instance.profile.phone
get_phone.short_description = 'Phone'
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal Info', {'fields': ('first_name', 'last_name',)}),
('Permissions', {'fields': ('admin', 'staff', 'active')}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'first_name', 'last_name', 'password1', 'password2', )
}
),
)
search_fields = ('email',)
ordering = ('email',)
filter_horizontal = ()
admin.site.register(User, UserAdmin)
# Remove Group Model from admin. We're not using it.
admin.site.unregister(Group)
form which is used to edit users in admin
from django import forms
from django.contrib.auth import get_user_model # or from .models import User
from django.contrib.auth.forms import ReadOnlyPasswordHashField
User = get_user_model() # this method will return the currently active user model
# or from .models import User
class UserAdminCreationForm(forms.ModelForm):
"""
A form for creating new users in admin panel. Includes all the required
fields, plus a repeated password.
"""
password1 = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ('first_name', 'last_name', 'email')
def clean_password2(self):
# Check that the two password entries match
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password2 and password1 != password2:
raise forms.ValidationError("Password don't match")
return password2
def save(self, commit=True):
# Save the provided password in hashed format
user = super(UserAdminCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data['password1'])
if commit:
user.save()
return user
class UserAdminChangeForm(forms.ModelForm):
"""
A form for updating users in admin panel. Includes all the fields on
the user, but replaces the password field with admin's
password hash display field.
"""
password = ReadOnlyPasswordHashField()
class Meta:
model = User
fields = ('first_name', 'last_name', 'email', 'password', 'active', 'staff', 'admin')
def clean_password(self):
# Regardless of what the user provides, return the initial value.
# This is done here, rather than on the field, because the
# field does not have access to the initial value
return self.initial['password']
I would recommend that you override the User model.
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, \
PermissionsMixin
class UserManger(BaseUserManager):
"""
Add extra calling functionalities here
"""
pass
class User(AbstractBaseUser, PermissionsMixin):
"""Custom user model"""
pass
objects = UserManger()
This is the basic format. Add the extra profile fields in the model
in setting.py add
AUTH_USER_MODEL = '{{ app_name }}.{{ model_name }}'
# eg. 'core.User'
I'm trying to write a REST API using Django and DRF. I'm trying to create a user model and use it in my application. But the problem is that it returns a 400 error status code which says:
{"username":["This field is required."]}
This is my code for models:
import uuid
from django.contrib.auth.models import AbstractUser
from django.db import models
from django.conf import settings
from django.dispatch import receiver
from django.utils.encoding import python_2_unicode_compatible
from django.db.models.signals import post_save
from rest_framework.authtoken.models import Token
from api.fileupload.models import File
#python_2_unicode_compatible
class User(AbstractUser):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
email = models.EmailField('Email address', unique=True)
name = models.CharField('Name', default='', max_length=255)
phone_no = models.CharField('Phone Number', max_length=255, unique=True)
address = models.CharField('Address', default='', max_length=255)
country = models.CharField('Country', default='', max_length=255)
pincode = models.CharField('Pincode', default='', max_length=255)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
def __str__(self):
return self.email
#receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
Token.objects.create(user=instance)
The Serializer:
class CreateUserSerializer(serializers.ModelSerializer):
username = None
def create(self, validated_data):
validated_data['username'] = uuid.uuid4()
user = User.objects.create_user(**validated_data)
return user
def update(self, instance, validated_data):
instance.name = validated_data.get('name', instance.name)
instance.address = validated_data.get('address', instance.address)
instance.country = validated_data.get('country', instance.country)
instance.pincode = validated_data.get('pincode', instance.pincode)
instance.phone_no = validated_data.get('phone_no', instance.phone_no)
instance.email = validated_data.get('email', instance.email)
instance.save()
return instance
class Meta:
unique_together = ('email',)
model = User
fields = (
'id', 'password', 'email', 'name', 'phone_no', 'address', 'country', 'pincode',
)
extra_kwargs = {'password': {'write_only': True}}
Admin.py file:
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import User
#admin.register(User)
class UserAdmin(UserAdmin):
pass
class User(AbstractUser):
As your User model inherits from AbstractUser, it will inherit
the username field.
Just remove the username field from your User model by setting username = None like this:
class User(AbstractUser):
# ...
username = None
# ...
class UserAdmin(UserAdmin):
As your UserAdmin model inherits from django.contrib.auth.admin.UserAdmin, you will need to update fieldsets, list_display, search_fields, and ordering fields in your UserAdmin model because they use username which you have removed from your User model.
Abstract User always has the username field. Removing it will cause problems. I will suggest you store the email address of the user in username field as well and use that. Please make sure its always updated in both fields which is not very hard.
I'm trying to add more fields to Django's default User model. I'm using the OneToOneField method. I want to create a signup page that allows the user to fill out a username, password, email, and other fields that I will add into a separate Profile model.
I've used some code snippets I've found and tried to make it work. However, I keep getting an IntegrityError at /account/signup/
UNIQUE constraint failed: accounts_profile.user_id error.
I think the problem is that right when the User gets created, a Profile gets created as well. Then When A new Profile gets created using the User's primary key it gives an error because a Profile for that primary key already exists.
Can someone show me the correct way to do this?
Here's all the relevant code:
models.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
class Profile(models.Model):
GENDER_CHOICES = (
('MALE', 'Male'),
('FEMALE', 'Female')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
middle_name = models.CharField(max_length=30, blank=True, default='')
prefix = models.CharField(max_length=6, blank=True, default='')
suffix = models.CharField(max_length=10, blank=True, default='')
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
forms.py
from django import forms
from django.forms import ModelForm
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = (
'username',
'first_name',
'last_name',
'email',
'password1',
'password2',
)
def save(self, commit=True):
user = super(RegistrationForm, self).save(commit=False)
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.email = self.cleaned_data['email']
if commit:
user.save()
return user
class ProfileForm(ModelForm):
class Meta:
model = Profile
fields = (
'middle_name',
'prefix',
'suffix',
)
def save(self, commit=True):
profile = super(ProfileForm, self).save(commit=False)
profile.middle_name = self.cleaned_data['middle_name']
profile.prefix = self.cleaned_data['prefix']
profile.suffix = self.cleaned_data['suffix']
if commit:
profile.save()
return profile
views.py
...
def signup_view(request):
if request.method == 'POST':
register = RegistrationForm(request.POST, prefix='register')
userprofile = ProfileForm(request.POST, prefix='profile')
print(register.is_valid())
print(userprofile.is_valid())
if register.is_valid() * userprofile.is_valid():
user = register.save()
profile = userprofile.save(commit=False)
print(user)
profile.user = user
profile.save()
return HttpResponse('congrats')
else:
return HttpResponse('errors')
else:
userform = RegistrationForm(prefix='register')
userprofileform = ProfileForm(prefix='profile')
return render(request, 'accounts/signup.html', {'userform': userform,
'userprofileform': userprofileform})