My forms.py is this.
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm,UserChangeForm
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()
And, i have to add "upload image" option for the user so that user could upload their image from any file and load it on the profile.
You have to extend your user model with field profile_image = models.FileField() there is tutorial how to do it.
Than you have to add
class RegistrationForm(UserCreationForm):
email= forms.EmailField(required = True)
profile_image = forms.FileField() # add this field
...
def save(self, commit=True):
...
user.profile_image = self.cleaned_data.get('profile_image')
...
and add enctype="multipart/form-data" attribute to the <form ...> tag in the html template. Here is tutorial from Django docs on File Uploads.
EDIT
Extending the User model in the most basic way without changing the actual User model is that you'll create new model with one-to-one relation to the user model.
from django.conf import settings
class Profile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
profile_image = models.FileField() # where is stored the user profile image
this is the Profile of each user. You will have to create profile when user is created, so:
place this code to the any models.py or file that is loaded on the startup with django
from django.contrib import auth
auth.models.User.add_to_class('create_profile', create_profile)
then create the method below
from (appname).models import Profile
def create_profile(self, **kwargs):
Profile.objects.create(
user=self,
**kwargs # you can pass other fields values upon creating
)
then modify to the RegistrationForm as I wrote before and modify the save function
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']
# user has to be saved to add profile
user.save()
user.create_profile()
user.profile_img = self.cleaned_data.get('profile_image')
user.profile.save()
if commit:
user.save()
also when you are instantiating the RegistrationForm with request.POST do it like
form = RegistrationForm(request.POST, request.FILES)
# to the files have to be passed also, they are not in request.POST
if form.is_valid():
form.save()
and thats it.
Related
I have a custom user model, I would like my CustomUser model to have a OneToOne relationship with the Person model; Since I want to first register persons and then create a username for them, since not all registered people need to have a username.
I have tried the code shown below; but what I get is the following error:
Cannot assign "1": "CustomUser.dni" must be a "Person" instance.
But, the ID number is already registered in the Person table
P.S. If anyone has a better suggestion for getting people to register first and then create a username for them when strictly necessary, I'd really appreciate it.
models.py
from .managers import CustomUserManager
# MODEL PERSON
class Person(models.Model):
dni = models.CharField('Número de DNI', max_length=8, unique=True)
...
# MODEL CUSTOMUSER
class CustomUser(AbstractBaseUser, PermissionsMixin):
dni = models.OneToOneField(Person, on_delete=models.CASCADE, unique=True)
email = models.EmailField('Correo electrónico', max_length=355, unique=True)
is_staff = models.BooleanField(default=True)
is_active = models.BooleanField(default=True)
is_superuser = models.BooleanField(default=False)
is_admin = models.BooleanField(default=False)
objects = CustomUserManager()
USERNAME_FIELD = 'dni'
REQUIRED_FIELDS = ['email']
...
managers.py
from django.contrib.auth.models import BaseUserManager
class CustomUserManager(BaseUserManager):
def create_user(self, dni, email, password=None):
if not dni:
raise ValueError('Debe ingresar el número de DNI.')
person = self.model(
dni=dni,
email=self.normalize_email(email),
)
person.set_password(password)
person.save(using=self._db)
return person
def create_superuser(self, dni, email, password=None):
person = self.create_user(
dni,
password=password,
email=email,
)
person.is_admin = True
person.is_superuser = True
person.save(using=self._db)
return person
settings.py
AUTH_USER_MODEL = 'accounts.CustomUser'
views.py
from accounts.forms import RegisterForm
class RegisterView(View):
form_class = RegisterForm
initial = {'key': 'value'}
template_name = 'accounts/register.html'
def get(self, request, *args, **kwargs):
form = self.form_class(initial=self.initial)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'Cuenta creada para exitosamente.')
return redirect(to='/')
return render(request, self.template_name, {'form': form})
I want to register a person in the Person table.
After creating the person, you should only be able to create their username and password, from the administration site; that is, an external person cannot register on the site, because the site will have many people in the database, but only some of them will be users of the system.
I hope this image helps clarify the idea.
Process flow
What you're probably looking to do is subclass the django registration form. Then you can change the fields that are required.
I believe the password field would be required by default so a NULL value would not be valid. However, you could quite easily set the password as a randomly generated UUID and then manually change it in the admin panel.
I am following a tutorial to do this in Django 3.1.7.
The problem I'm having here is I'm being forced to repeat my Profile Model in my Profile Form definition.
I want to use forms.ModelForm in my forms.py to inherit my Profile Model and auto-generate the forms. It seems redundant to have to spell everything out again in forms.py when it is already defined in my Models. But I'm not sure how to do that with this architecture.
I've tried this approach:
https://stackoverflow.com/a/2213802/4144483
But the problem with this is that UserForm is incomplete - 'password1' and 'password2' don't exist for model User. This is not a good solution for user registration. I seem to be bound to using UserCreationForm somehow.
#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 Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
bio = models.TextField(max_length=500, blank=True)
location = models.CharField(max_length=30, blank=True)
birth_date = models.DateField(null=True, blank=True)
#receiver(post_save, sender=User)
def update_user_profile(sender, instance, created, **kwargs):
if created:
Profile.objects.create(user=instance)
instance.profile.save()
#forms.py
rom django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class SignUpForm(UserCreationForm):
birth_date = forms.DateField(help_text='Required. Format: YYYY-MM-DD')
class Meta:
model = User
fields = ('username', 'birth_date', 'password1', 'password2', )
#views.py
from django.contrib.auth import login, authenticate
from django.shortcuts import render, redirect
from mysite.core.forms import SignUpForm
def signup(request):
if request.method == 'POST':
form = SignUpForm(request.POST)
if form.is_valid():
user = form.save()
user.refresh_from_db() # load the profile instance created by the signal
user.profile.birth_date = form.cleaned_data.get('birth_date')
user.save()
raw_password = form.cleaned_data.get('password1')
user = authenticate(username=user.username, password=raw_password)
login(request, user)
return redirect('home')
else:
form = SignUpForm()
return render(request, 'signup.html', {'form': form})
I generally use ModelForm instead of CreateUserForm for UserRegistration like this and add password1 and password2 fields in it. also, I check if they both are the same.:
forms.py
class UserRegistrationForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat Password', widget=forms.PasswordInput)
email = forms.EmailField(label='Email')
date_of_birth = forms.DateField(widget=forms.widgets.DateInput(attrs={'type': 'date'}))
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email',
] #these ordering will be as follow in html form
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError("Passwords don't match")
return cd['password2']
Then in views, I create a user and their profile and save the password in encrypted form, and link their profile.
views.py
def register(request):
u_form = UserRegistrationForm(data=request.POST or None)
p_form = ProfileForm(data=request.POST or None, files=request.FILES or None)
if u_form.is_valid() and p_form.is_valid():
new_user = u_form.save(commit=False)
new_user.set_password(u_form.cleaned_data['password']) #this saves password in encrypted form instead of raw password
new_user.save()
profile = p_form.save(commit=False)
profile.user = new_user
profile.save()
return render(request, 'accounts/register_done.html', {'new_user': user})
return render(request, 'accounts/register.html', {'user_form': u_form, 'profile_form':p_form})
You can modify it as you like.
I'm trying to make a form for a service providers booking app as part of a project. I'm just wondering can I change the default "Username" field in Django forms to "Job Title".
Here is my current "Forms.py file.
from django import forms
from django.contrib.auth.models import User
from django.contrib.auth.forms import UserCreationForm
from .models import Profile
class UserRegisterForm(UserCreationForm):
email = forms.EmailField()
class Meta:
model = User
fields = ('username',
'email',
'first_name',
'last_name',
'password1',
'password2'
)
def save(self, commit=True):
user = super(UserRegisterForm, self).save(commit=False)
user.first_name = cleaned_data['first_name']
user.last_name = cleaned_data['last_name']
user.email = cleaned_data['email']
if commit:
user.save()
return user
The error I get is: extend user to a custom form, the "user_id" field is my custom form is the "property", which is linked to the table "auth_user" is not saved, and I need both tables relate to make use my custom form attributes and shape of the User of django.
my models.py
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.db import models
# Create your models here.
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
Matricula = models.CharField(max_length=25)
forms.py
class SignupForm(forms.ModelForm):
class Meta:
model = Profile
fields = ('first_name', 'last_name', 'Matricula')
#Saving user data
def signup(self, request, user):
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
user.Matricula = self.cleaned_data['Matricula']
user.save()
##Save profile
profile = Profile()
Profile.user = user
profile.Matricula = self.cleaned_data['Matricula']
profile.save()
i tried:
user = models.OneToOneField(User, on_delete=models.CASCADE)
but I get an error:
Error
You believe that ForeignKey can be used or correct use OneToOneField?
You should be careful with your capitalisation. You've assigned the user value to the class, not the instance. It should be:
profile = Profile()
profile.user = user
Or better:
profile = Profile(user=user)
You are not setting user to that instance of Profile:
profile = Profile()
profile.user = user # Notice the case of Profile
I've successfully created and extended the UserCreationForm to get email, first_name & last_name. It works fine in inserting all values. But I think the password field is not being saved. I'm using the built-in auth object to authenticate the user to login to my site. When the use register in the site and tries to login the password doesn't matches and the user cannot login. When I change the password from admin, I can login successfully. So, can anyone help me?
This is how I've extended UserCreationForm:
from django import forms
from django.contrib.auth.forms import User
from django.contrib.auth.forms import UserCreationForm
class RegistrationForm(UserCreationForm):
email = forms.EmailField(required=True)
first_name = forms.TextInput()
last_name = forms.TextInput()
class Meta:
model = User
fields = ("username", "first_name", "last_name", "email", "password1", "password2")
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user
Now, I click the form link and I change the password.
The problem
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
^
|___ super of UserCreationForm is ModelForm!
Your code is calling super of UserCreationForm and not the super of your own form RegistrationForm. (The super of UserCreationForm being ModelForm has no idea how to handle user registration like password saving)
The result is that user.set_password() wasn't being called anywhere
However, I'd say no need to call user.set_password again since your super already does.
UserCreationForm.save() is defined as follows:
def save(self, commit=True):
user = super(UserCreationForm, self).save(commit=False)
user.set_password(self.cleaned_data["password1"])
if commit:
user.save()
return user
Source code on github
Solution
You should have something like this
def save(self, commit=True):
# Call save of the super of your own class,
# which is UserCreationForm.save() which calls user.set_password()
user = super(RegistrationForm, self).save(commit=False)
# Add the things your super doesn't do for you
user.email = self.cleaned_data['email']
user.first_name = self.cleaned_data['first_name']
user.last_name = self.cleaned_data['last_name']
if commit:
user.save()
return user