I extended my User Model as described in this SO Posting:
Extending the User model with custom fields in Django
However, I'm trying to create a User Create form but I get the following:
'Members' object has no attribute 'set_password'
Here is my model form:
class Members(models.Model):
user = models.OneToOneField(User)
GENDER_CHOICES = ( ... )
gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
date_of_birth = models.DateField()
class Meta:
db_table='members'
def create_user_profile(sender, instance, created, **kwargs):
if created:
Members.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User)
....and my form....
class SignUpForm(UserCreationForm):
GENDER_CHOICES = ( ... )
email = forms.EmailField(label='Email address', max_length=75)
first_name = forms.CharField(label='First Name')
last_name = forms.CharField(label='Last Name')
gender = forms.ChoiceField(widget=RadioSelect, choices=GENDER_CHOICES)
date_of_birth = forms.DateField(initial=datetime.date.today)
class Meta:
model = Members
fields = ('username', 'email','first_name', 'last_name')
I'm new at Django,so thanks in advance
The method you chose to extend your User model is by creating a UserProfile (which you've called Member). A Member is not a subclass of User, so you can't call User methods (like set_password) on it.
Instead, your SignUpForm's Meta model should still be User, and to get the extended UserProfile, you should call user.get_profile(). For instance, to get a user's gender, you would call user.get_profile().gender.
Read https://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users for more information about extending the user profile.
Related
I've created a custom user abstract model and profile model to collect additional information once the user registers.
I am collecting "User type: Employer/employee" at the time of registration but this doesn't seem to be recognized in the profile view. Despite the user being correctly added into the DB (I checked via Admin).
For example, I created user: asus23910 (employer user type). But when I login and redirect to http://127.0.0.1:8000/employer_profile/asus23910/, I get following error:
'User' object has no attribute 'user_type'C:\Users\ASUS\PycharmProjects\Content\content\content\views.py, line 112, in employer_profile_view
1. Here's my employer_profile_view.py code:
def employer_profile_view(request, username):
user = User.objects.get(username=username)
if user.user_type != User.EMPLOYER:
# Redirect to the correct profile page if the user type is not employer
return redirect('employee_profile', username=request.user.username)
if request.method == 'POST':
form = EmployerProfileForm(request.POST, instance=user.employerprofile)
if form.is_valid():
employer_profile = form.save(commit=False)
employer_profile.user = user
employer_profile.save()
return redirect('employer_profile', username=request.user.username)
else:
form = EmployerProfileForm(instance=user.employerprofile)
context = {
'form': form,
'username': username,
}
return render(request, 'employer_profile.html', context)
2. Employer Profile model and connector
class EmployerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
user_type = models.CharField(
max_length=10,
choices=User.USER_TYPE_CHOICES,
default=User.EMPLOYER
)
first_name = models.CharField(max_length=255)
last_name = models.CharField(max_length=255)
title = models.CharField(max_length=255)
company_name = models.CharField(max_length=255)
company_logo = models.ImageField(upload_to='company_logos/')
company_location = models.CharField(max_length=255)
company_website = models.URLField()
company_twitter = models.URLField()
#one-2-one connector
#receiver(post_save, sender=User)
def create_employer_profile(sender, instance, created, **kwargs):
if created:
EmployerProfile.objects.create(user=instance, user_type=instance.user_type)
print('Employer Profile created')
#receiver(post_save, sender=User)
def save_employer_profile(sender, instance, **kwargs):
instance.employerprofile.user_type = instance.user_type
instance.employerprofile.save()
print('Employer Profile saved')
3. User model
#model one to store the user into db
class User(AbstractUser):
EMPLOYER = "employer"
EMPLOYEE = "employee"
USER_TYPE_CHOICES = [
(EMPLOYER, "Employer"),
(EMPLOYEE, "Employee"),
]
user_type = models.CharField(
max_length=10,
choices=USER_TYPE_CHOICES,
default=EMPLOYEE
)
email = models.EmailField(default='example#example.com')
username = models.CharField(max_length=150, default='example_user')
password = models.CharField(max_length=128, default='!')
groups = models.ManyToManyField(
Group,
blank=True,
related_name='content_groups'
)
user_permissions = models.ManyToManyField(
Permission,
blank=True,
related_name='content_user_permissions'
)
`
**
What I've tried:**
Flushing and starting new DB (as I used in-built Django user model before and some old users weren't fairing well with the new user-type field).
Adding the user type with default employer option to employer view and fetching the usertype from user model.
**
What I expect:**
The profile view to connect with the custom user model and allow the user to add additional information to their user profile. And ofcourse the profile page to have the user-type attribute as initially stored from user class.
You probably import User not from your models file, but from django.
Anyway I highly recommend (if you overwrote AUTH_USER_MODEL) using built-in get_user_model() method from Django e.g.:
from django.contrib.auth import get_user_model
def employer_profile_view(request, username):
user = get_user_model().objects.get(username=username)
if user.user_type != User.EMPLOYER:
...
And don't use User name for model, I prefer to use CustomUser by myself but you can name it differently, just to avoid mistakes.
To begin with, Here Profile and Seller model is created when a User model is created through Signals.What I want to do is When profile model is first created or updated,I want all the fields of Seller model to be same as all fields of Profile.Similarly when I first created Seller model,I also want all fields of Profile Model to be same as that of Seller model.But,I couldn't figure out how to do?
from typing import Tuple
from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.db.models.fields import DecimalField
from django.dispatch import receiver
from django.db.models.signals import post_save
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
#receiver(post_save, sender=User)
def create_profile(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Profile.objects.create(user=instance)
...
#receiver(post_save, sender=Profile)
def create_seller(sender, instance,created,**kwargs):#Signal receivers must accept keyword arguments (**kwargs).
if created:
Seller.objects.create(user=instance)
class Seller(models.Model):
user = models.OneToOneField(Profile, on_delete=models.CASCADE, null=True, blank=True) #cascade is for deleting the customer
first_name = models.CharField(max_length=10, null=True)
second_name=models.CharField(max_length=10,null=True)
email = models.EmailField(max_length=70, null=True,blank=True)
...
If created=True it means a Profile object is created. otherwise if created=False it means that a Profile object has been updated.
When created=True you need to create a Seller object and when created=False you need to update a Seller object.
You can do this:
#receiver(post_save, sender=Profile)
def update_or_create_seller(sender, instance, created, **kwargs):
if created:
Seller.objects.create(
user=instance.user,
first_name=instance.first_name,
second_name=instance.second_name,
email=instance.email
)
else:
seller = instance.user.seller
seller.first_name = instance.first_name
seller.last_name = instance.second_name
seller.email = instance.email
seller.save()
Also notice that unless you define related_name in your OneToOneField, Django will use lowercased model name to access related object. So, instance.user.seller should work.
I want the user to be able to update their username but not email address. In example 3 despite the fact I do not include the field email in my code, the field still appears when I run the site. Admittedly the text box is blank whereas in example 1 and 2 it is populated.
How can I stop the email text box appearing? Or can I lock it so the user cannot enter a new value?
Example 1
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username', 'email']
Example 2
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['email']
Example 3
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField()
class Meta:
model = User
fields = ['username']
from django.forms import ModelForm
class UserUpdateForm(ModelForm):
#email = forms.EmailField()
class Meta:
model = User
fields = ['username']
Since you need all other fields except email (or at least you mentioned only email as the one you'd like to hide), perhaps you can use then exclude attribute:
from django.forms import ModelForm
class UserUpdateForm(ModelForm):
class Meta:
model = User
exclude = ('email',)
For more details you can read in this doc.
Also, alternative way to go is to disable field (not checked):
class UserUpdateForm(forms.ModelForm):
email = forms.EmailField(disabled=True)
class Meta:
model = User
fields = ['username', 'email']
Passing disabled=True should still render the field but unable it edition by user.
I have a custom user model CustomUser and related models Employee with Foreignkey field to user.
class Employee(models.Model):
"""Employee information."""
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, unique=True)
first_name = models.CharField("first name", max_length=32, blank=True)
last_name = models.CharField("last name", max_length=32, blank=True)
I try to create Employee instance after user registration using post_save signals with fields first_name and last_name imported from my user model fields.
#receiver(post_save, sender=CustomUser)
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
if instance.first_name and instance.last_name:
employee = Employee.objects.create(user=instance, first_name=instance.cleaned_data['first_name'], last_name=instance.clened_data['last_name'] )
else:
employee = Employee.objects.create(user=instance)
But always created model with blank field. What is the reason for this and what needs to be changed?
p.s. I need the same filds at both models for some reason.
I am using Django 1.10.*. This is my user profile model:
class student(models.Model):
user = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
state = models.CharField(max_length=21, null=True, blank=True, choices=in_states.STATE_CHOICES)
city = models.CharField(max_length=21, null=True, blank=True)
date_joined = models.DateTimeField(default=timezone.now)
educational_role = models.CharField(max_length=39, choices=EDUCATIONAL_ROLE)
institute = models.ForeignKey(educational_institute, null=True, blank=True)
language = models.CharField(max_length=8, choices=LANGUAGES)
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
student.objects.create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.profile.save()
When I created form class for student and use that to create a View class extending FormView class in my Views passing the form context to HTML template like this:
forms.py :
class RegistrationForm(forms.ModelForm):
class Meta:
model = student
fields = ['user', 'state', 'city', 'educational_role', 'institute', 'language']
views.py :
class Register(FormView):
template_name = 'searcher/register.html'
form_class = RegistrationForm
def get_context_data(self, **kwargs):
context = super(Register, self).get_context_data(**kwargs)
context['applink'] = applink
context['forumlink'] = forumlink
return context
So how do I need to modify this so that the registration form asks for username, firstname, email of User model and also the fields added by student model and having option for creating a new educational_institute if it doesn't exist already?
You could use multi-table inheritance django.contrib.auth.models.User. Would look like this:
from django.contrib.auth.models import User
class Student(User):
state = models.CharField(...)
...
This way all Django features like ModelForm or UpdateView should function without any additional fiddling. In behind, Django will just create exactly the same OneToOneField for you.
And if all of your users are students you could also replace the auth user model entirely.
If you don't want to do any of those, you will need to add additional fields into your form, specify them if you form Meta, take care of setting their default values (in __init__ in example), cleaning these fields and saving the model behind OneToOneField all by yourself.