How to exclude fields from form prefilled with object - python

How to exclude some fields from form created in view from instance?
I want to allow users to edit their attributes like username or telephone but in this form, they shouldn't change their password.
I've tried this:
del user_profile_form.fields['telephone']
But it raises CSRF token missing or incorrect. when I do that.
#login_required
def edit_profile(request):
user = request.user
user_form = UserForm(instance=user)
user_profile_form = UserProfileForm(instance=user.userprofile)
context = {'user_form': user_form,
'user_profile_form': user_profile_form}
return render(request, 'auth/profiles/edit-profile.html', context=context)
FORMS.PY
class UserForm(forms.ModelForm):
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password1','password2', 'first_name', 'last_name')
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return self.cleaned_data
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('telephone','marital_status','how_do_you_know_about_us')
MODELS.PY
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE,related_name='userprofile')
# ATRIBUTY KTORE BUDE MAT KAZDY
telephone = models.CharField(max_length=40,null=True)
HOW_DO_YOU_KNOW_ABOUT_US_CHOICES = (
('coincidence',u'It was coincidence'),
('relative_or_friends','From my relatives or friends'),
)
how_do_you_know_about_us = models.CharField(max_length=40, choices=HOW_DO_YOU_KNOW_ABOUT_US_CHOICES, null=True)
MARITAL_STATUS_CHOICES = (
('single','Single'),
('married','Married'),
('separated','Separated'),
('divorced','Divorced'),
('widowed','Widowed'),
)
marital_status = models.CharField(max_length=40, choices=MARITAL_STATUS_CHOICES, null=True)
# OD KIAL STE SA O NAS DOZVEDELI
# A STAV
def __unicode__(self):
return '{} {}'.format(self.user.first_name,self.user.last_name)
def __str__(self):
return '{} {}'.format(self.user.first_name,self.user.last_name)
NEW VIEW
#login_required
def edit_profile(request):
user = request.user
if request.method == 'POST':
user_form = UserForm(request.POST)
user_profile_form = UserProfileForm(request)
if user_form.is_valid() and user_profile_form.is_valid():
user_form.save()
user_profile_form.save()
return HttpResponseRedirect('/logged-in')
else:
print user_form.errors
print user_profile_form.errors
else:
user_form = UserForm(instance=user)
user_profile_form = UserProfileForm(instance=user.userprofile)
temp_user_profile_form = deepcopy(user_profile_form)
del temp_user_profile_form.fields['password1']
del temp_user_profile_form.fields['password2']
context = {'user_form': user_form,
'user_profile_form': temp_user_profile_form}
return render(request, 'auth/profiles/edit-profile.html', context=context)
ERROR
Exception Type: KeyError
Exception Value:
'password1'

It looks like you're referencing password1 and password2 in your Meta class for the UserForm model form. These should be removed, as they aren't fields in your User model. So after the change, your UserForm should be:
class UserForm(forms.ModelForm):
# These 2 fields are unbound fields...
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
# These fields are your User model's fields
fields = ('username', 'email', 'first_name', 'last_name')
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return self.cleaned_data
You don't need to remove them in the view. Just exclude them in the template.
Additionally, you can make the form fields hidden inputs in the __init__ method for your form if you'd like. I'd recommend this approach.

Related

User Login Authentication using Django Model and form

I am trying to setup user authentication for the login page using forms and comparing it to my database value but it does not work. I also tried using this particular questions User Login Authentication using forms and Django logic to solve my problem but it didn't help.
Models.py
from django.db import models
from django.contrib.auth.password_validation import validate_password
class student(models.Model):
first_name = models.CharField(max_length=150)
last_name = models.CharField(max_length=150)
matric_number = models.CharField(max_length=9)
email = models.EmailField(max_length=50)
password1 = models.CharField(max_length=255, validators=[validate_password])
password2 = models.CharField(max_length=255)
def __str__(self):
return (self.matric_number)
This view saves user info to database
def student(request):
if request.method == 'POST':
form = studentForm(request.POST)
if form.is_valid():
sign_up = form.save(commit=False)
#sign_up.password1 = make_password(form.cleaned_data['password1'])
#sign_up.password2 = make_password(form.cleaned_data['password2'])
sign_up.status = 1
sign_up.save()
user = form.cleaned_data.get('matric_number')
messages.success(request, "Account was created for "+str(user))
return redirect(signin)
else:
form = studentForm()
return render(request, 'Student.html',{
"form": form
})
This is the signin view
def signin(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
try:
student = student.object.get(username=username, password=password)
return redirect(files)
except:
messages.success(request, "Error")
else:
form = LoginForm()
return render(request, "SignIn.html",{
"form":form
})
This is my form.py
class studentForm(forms.ModelForm):
class Meta:
model=student
fields="__all__"
widgets={
'first_name':forms.TextInput(attrs={'placeholder': 'Enter Your First Name'}),
'last_name':forms.TextInput(attrs={'placeholder': 'Enter Your Last Name'}),
'matric_number':forms.TextInput(attrs={'placeholder': 'Enter Your Matric Number'}),
'email':forms.EmailInput(attrs={'placeholder': 'abc#example.com'}),
'password1':forms.PasswordInput(attrs={'placeholder': 'Enter Your Preferred Password','id':'password'}),
'password2':forms.PasswordInput(attrs={'placeholder':'Confirm Your Password', 'id':'password1'})
}
def clean(self):
super(studentForm, self).clean()
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
matric_number = self.cleaned_data.get('matric_number')
email = self.cleaned_data.get('email')
try:
if password1 != password2:
self.errors[''] = self.error_class(["The two password fields must match"])
elif len(matric_number) != 9:
self.errors[''] = self.error_class(["You have entered an invalid matric number"])
elif len(matric_number) == 9:
matric_number = int(matric_number)
except ValueError:
self.errors[''] = self.error_class(["You have entered an invalid matric number"])
for instance in student.objects.all():
if instance.matric_number == str(matric_number):
self.errors[''] = self.error_class(["Matric number already exist"])
elif instance.email == email:
self.errors[''] = self.error_class(["E-mail address already exist"])
class LoginForm(forms.Form):
matric_number = forms.CharField(max_length=9, widget=forms.TextInput(attrs={'id': 'username', 'placeholder': 'Enter Your Staff Id Or Matric Number'}))
password1 = forms.CharField(max_length=9, widget=forms.PasswordInput(attrs={'id': 'password', 'placeholder':'Enter Your password'}))
Stop reinventing the wheel. Also, class names are supposed to be named with PascalCase.
Use AbstractUser model:
from django.contrib.auth.models import AbstractUser
class Student(AbstractUser):
...
and in your main urls.py:
from django.contrib.auth import views as auth_views
urlpatterns = [
...
path('login/', auth_views.LoginView.as_view(), name='login'),
...
]
It is much faster and SAFER way to create new user.
So I figured out out how to solve my problem. By using the AbstractUser model,i was able to create a custom user and then create another model which i extended a ForeignKey on the User model therefore allowing me to tie every user to their profile.
Here is my models.py
from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.
class User(AbstractUser):
pass
def __str__(self):
return self.username
class UserProfile(models.Model):
"""
This is the one for model.py
"""
username = models.ForeignKey(User, on_delete=models.CASCADE, null=True, default="")
profile_picture = models.ImageField(blank=True, null=True, default="")
matricno = models.CharField(max_length=9, default="", primary_key=True)
email = models.EmailField(default="")
first_name = models.CharField(max_length=200, default="")
last_name = models.CharField(max_length=255, default="")
class Meta:
verbose_name_plural = "Users Profile"
def __str__(self):
return self.first_name+ " "+self.last_name
And here is my views.py
def signup(request):
if request.method == "POST":
form = Signup(request.POST)
if form.is_valid():
username = request.POST["username"]
email = request.POST["email"]
password = request.POST["password"]
password2 = request.POST["password2"]
user = User.objects.create_user(
username=username,
password=password,
email=email,
)
user.save()
login(request, user)
messages.success(request, "Account Created successfully for " + username)
return redirect(details)
else:
form = Signup()
return render(request, "accounts/register.html", {"form": form})
def details(request, username):
user = User.objects.get(username=username)
form = Details()
if request.method == "POST":
form = Details(request.POST, request.FILES)
if form.is_valid():
detail = form.save(commit=False)
detail.username = request.user
detail.save()
return redirect(success, pk=detail.pk)
else:
form = Details(initial={"matricno":request.user.username})
return render(request, "details.html", {"form":form})
And finally my forms.py that i use in creating a signup form and perfoming validation
class Signup(forms.Form):
username = forms.CharField(
max_length=9,
widget=forms.TextInput(attrs={"placeholder": "Enter Your Matric Number"}),
)
email = forms.EmailField(
max_length=255,
widget=forms.EmailInput(attrs={"placeholder": "Enter Your E-mail Address"}),
)
password = forms.CharField(
max_length=255,
widget=forms.PasswordInput(
attrs={"placeholder": "Enter Your Password", "id": "password"}
),
)
password2 = forms.CharField(
max_length=255,
widget=forms.PasswordInput(
attrs={"placeholder": "Confirm Your Password", "id": "password2"}
),
)
def clean(self):
super(Signup, self).clean()
password = self.cleaned_data.get("password")
password2 = self.cleaned_data.get("password2")
username = self.cleaned_data.get("username")
email = self.cleaned_data.get("email")
if password != password2:
self.errors[""] = self.error_class(["The two password fields must match"])
for instance in User.objects.all():
if instance.username == str(username):
self.errors[""] = self.error_class(["User already exist"])
elif instance.email == email:
self.errors[""] = self.error_class(["E-mail already in use"])
else:
pass
return self.cleaned_data

django sign up form not storing the username

I have a sign up form that does not store the username properly, other fields are stored successfully. Shouldn't it save the username because it is in the fields ? I saw that I cannot login and this is because the row in the table does not have the username.
views.py
class SignUpFormView(FormView):
form_class = SignUpForm
template_name = 'users/authentication/login/signup.html'
def post(self, request):
form = self.form_class(data=request.POST)
if form.is_valid():
user = form.save(commit=False)
# some logic here
user.save()
messages.success(
request, "Great! You are able to login now.")
return redirect('login')
else:
messages.error(request, form.errors)
return redirect('login')
forms.py
class CustomUserCreationForm(UserCreationForm):
def clean_username(self):
username = self.cleaned_data["username"]
try:
TbUser.objects.get(username=username)
except TbUser.DoesNotExist:
return username
raise forms.ValidationError(self.error_messages['duplicate_username'])
class Meta(UserCreationForm.Meta):
model = TbUser
class SignUpForm(CustomUserCreationForm):
email = forms.EmailField()
customer_id = forms.CharField(label="Customer ID")
def clean_username(self):
data = self.cleaned_data['username']
if TbUser.objects.filter(username=data).exists():
raise forms.ValidationError(
"Username already exists. Pick another one")
def clean(self):
cd = self.cleaned_data
password1 = cd.get("password1")
password2 = cd.get("password2")
if password1 != password2:
raise ValidationError("Passwords did not match")
return cd
class Meta:
model = TbUser
fields = ['username', 'email', 'real_name', 'customer_id',
'password1', 'password2']
The clean_username method of SignUpForm does not return anything. You need to return cleaned data for it. So add return data to the bottom of this method.

TypeError: __init__() got an unexpected keyword argument 'instance' for custom user in Django

When I am trying to register a custom user or when i am trying to view profile of login user, i am getting this error. I am not sure why i am getting this error, Here is my code, please suggest where i am doing wrong. Thank you in advance.
urls.py
# urls.py
path('register/', RegisterView.as_view(template_name='users/register.html'), name='register'),
path('profile/', user_views.profile, name='profile'),
models.py
# models.py
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(default='default.jpg', upload_to='profile_pics')
def __str__(self):
return f'{self.user.username} Profile'
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
img = Image.open(self.image.path)
if img.height > 300 or img.width > 300:
output_size = (300, 300)
img.thumbnail(output_size)
img.save(self.image.path)
class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
username = models.CharField(max_length=254, unique=True)
active = models.BooleanField(default=True)
staff = models.BooleanField(default=False) # a admin user; non super-user
admin = models.BooleanField(default=False) # a superuser
# notice the absence of a "Password field", that is built in.
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Email & Password are required by default.
objects = UserManager() ```
forms.py
# forms.py
class RegisterForm(forms.Form):
password = forms.CharField(widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm password', widget=forms.PasswordInput)
class Meta:
Model = User
fields = ('email',)
def clean_email(self):
email = self.cleaned_data.get('email')
qs = User.objects.filter(email=email)
if qs.exists():
raise forms.ValidationError("email is taken")
return 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("Passwords 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"])
# user.active = False
if commit:
user.save()
return user
class UserUpdateForm(forms.Form):
email = forms.EmailField()
class Meta:
Model = User
fields = ['username', 'email']
class ProfileUpdateForm(forms.Form):
class Meta:
Model = Profile
fields = ['image'] ```
views.py
# views.py
#login_required
def profile(request):
if request.method == 'POST':
u_form = UserUpdateForm(request.POST, instance=request.user)
p_form = ProfileUpdateForm(request.POST, request.FILES, instance=request.user.profile)
if u_form.is_valid() and p_form.is_valid():
u_form.save()
p_form.save()
messages.success(request, f'Your account has been updated!!')
return redirect('profile')
else:
u_form = UserUpdateForm(instance=request.user)
p_form = ProfileUpdateForm(instance=request.user.profile)
context = {
'u_form': u_form,
'p_form': p_form
}
return render(request, 'users/profile.html', context)
class RegisterView(CreateView):
form_class = RegisterForm
template_name = 'users/register.html'
success_url = 'users/login.html',
Identify what function is throwing this error and try to add an __init__ method that receive yours arguments.
def __init__(self, *args, **kwargs):

How to allow user to change his attributes?

I'm trying to figure out how to allow users to change their profile. I have a Users extended by User Profile (OneToOne).
I was thinking about changing registration view, prefill user's attributes and allow him to change them. But it is not probably the good way.
Could you give me a hint how to do that?
class UserForm(forms.ModelForm):
password1 = forms.CharField(widget=forms.PasswordInput())
password2 = forms.CharField(widget=forms.PasswordInput())
class Meta:
model = User
fields = ('username', 'email', 'password1','password2', 'first_name', 'last_name')
def clean(self):
password1 = self.cleaned_data.get('password1')
password2 = self.cleaned_data.get('password2')
if password1 and password1 != password2:
raise forms.ValidationError("Passwords don't match")
return self.cleaned_data
class UserProfileForm(forms.ModelForm):
class Meta:
model = UserProfile
fields = ('telephone','marital_status','how_do_you_know_about_us')
MODELS.PY
class UserProfile(models.Model):
user = models.OneToOneField(User,on_delete=models.CASCADE)
# ATRIBUTY KTORE BUDE MAT KAZDY
telephone = models.CharField(max_length=40,null=True)
HOW_DO_YOU_KNOW_ABOUT_US_CHOICES = (
('coincidence',u'It was coincidence'),
('relative_or_friends','From my relatives or friends'),
)
how_do_you_know_about_us = models.CharField(max_length=40, choices=HOW_DO_YOU_KNOW_ABOUT_US_CHOICES, null=True)
MARITAL_STATUS_CHOICES = (
('single','Single'),
('married','Married'),
('separated','Separated'),
('divorced','Divorced'),
('widowed','Widowed'),
)
marital_status = models.CharField(max_length=40, choices=MARITAL_STATUS_CHOICES, null=True)
# OD KIAL STE SA O NAS DOZVEDELI
# A STAV
def __unicode__(self):
return '{} {}'.format(self.user.first_name,self.user.last_name)
def __str__(self):
return '{} {}'.format(self.user.first_name,self.user.last_name)
REGISTRATION VIEW:
def register(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
profile_form = UserProfileForm(request.POST)
if user_form.is_valid() and profile_form.is_valid():
user = user_form.save()
user.set_password(user_form.cleaned_data['password1'])
user.save()
profile = profile_form.save(commit=False)
profile.user = user
profile.save()
return register_success(request)
else:
print user_form.errors, profile_form.errors
else:
user_form = UserForm()
profile_form = UserProfileForm()
return render(request, "auth/registration/register.html",
context={'user_form': user_form, 'profile_form': profile_form})
EDIT:
This is the view I'm trying to create but it does not autofill form:
#login_required
def edit_profile(request):
myUser = request.user
user_form = UserForm(request.POST, instance=myUser)
user_profile_form = UserProfileForm(request.POST, instance=myUser)
context={'user_form': user_form,
'user_profile_form':user_profile_form}
return render(request, 'auth/profiles/my_profile.html', context=context)
In the edit_profile view you added, you're passing your forms a POST request argument. You should only be passing this argument on a POST request. So update your forms to be the following if the request is a GET request:
views.py
user_form = UserForm(instance=myUser)
user_profile_form = UserProfileForm(instance=myUser)
forms.py
# Something like this will only save password if data is entered in one of the password fields
def clean(self):
cleaned_data = super(UserForm, self).clean()
password1 = cleaned_data.get('password1', None)
password2 = cleaned_data.get('password2', None)
old_password = cleaned_data.get('old_password', None)
if password1 or password2:
if password1 != password2:
self._errors['password1'] = 'New Password and Confirm New Password must match.'
self._errors['password2'] = 'New Password and Confirm New Password must match.'
if not self.user.check_password(old_password):
self._errors['old_password'] = 'Your old password was entered incorrectly.'
return cleaned_data
def save(self, request):
user = self.user
if self.cleaned_data.get('password1', None):
user.set_password(self.cleaned_data.get('password1'))
update_session_auth_hash(request, user)
user.save()
return user
For your choices issue, you can specify choices as an argument when specifying widgets for the field.

Django - duplicate key value violates unique constraint

In my Django project i create an app to have additional information about registered users. So my model looks like this:
class UserProfile(models.Model):
class Meta:
verbose_name_plural = u'User Profile'
user = models.OneToOneField(User)
birthday = models.DateField(blank=True, null=True)
avatar = models.ImageField(upload_to='media/profile/avatar', blank=True, null=True)
name = models.CharField(blank=True, null=True, max_length=20)
surname = models.CharField(blank=True, null=True, max_length=50)
phone = models.CharField(blank=True, null=True, max_length=12)
def __unicode__(self):
return '%s' % self.user
Here is my registration form:
class RegistrationForm(ModelForm):
username = forms.CharField(label=(u'Username'))
email = forms.EmailField(label=(u'Email'))
password = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
password1 = forms.CharField(label=(u'Password'), widget=forms.PasswordInput(render_value=False))
class Meta:
model = UserProfile
exclude = ('user',)
fields = ('username', 'email', 'password', 'password1')
def clean_email(self):
email = self.cleaned_data['email']
try:
User.objects.get(email=email)
except User.DoesNotExist:
return email
raise forms.ValidationError("User with same email already exist, please change your email")
And here is view of my registration form:
def UserProfileRegistration(request):
if request.user.is_authenticated():
return HttpResponseRedirect('/profile/')
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
user = User.objects.create_user(username=form.cleaned_data['username'], email=form.cleaned_data['email'], password=form.cleaned_data['password'])
user.save()
user_profile = UserProfile(user=user)
user_profile.save()
return HttpResponseRedirect('/profile/')
else:
return render(request, 'profiles/registration.html', {'form':form})
else:
form = RegistrationForm()
context = {'form':form}
return render (request, 'profiles/registration.html', context)
In user profile i create modelform where user can fill the fields from UserProfile model:
class ExtraProfileDataForm(ModelForm):
name = forms.CharField(label=(u'Enter your name'))
surname = forms.CharField(label=(u'Enter your surname'))
phone = forms.CharField(label=(u'Enter your phone'))
birthday = forms.DateField(label=(u'Enter birthday'))
avatar = forms.ImageField(label=(u'Enter avatar'))
class Meta:
model = UserProfile
fields = ('name', 'surname', 'phone', 'birthday', 'avatar')
def __init__(self, *args, **kwargs):
super(ExtraProfileDataForm, self).__init__(*args, **kwargs)
for key in self.fields:
self.fields[key].required = False
This is the view of the model form:
#login_required
def UserFullDataForm(request):
if request.method == 'POST':
form = ExtraProfileDataForm(request.POST)
if form.is_valid():
profile_user = request.user
user_profile = UserProfile(user=profile_user)
user_profile.name = form.cleaned_data['name']
user_profile.surname = form.cleaned_data['surname']
user_profile.phone = form.cleaned_data['phone']
user_profile.birthday = form.cleaned_data['birthday']
user_profile.avatar = form.cleaned_data['avatar']
user_profile.save()
return HttpResponseRedirect('/profile/')
else:
return render(request, 'profiles/extra_profile.html', {'form':form})
else:
form = ExtraProfileDataForm()
context = {'form':form}
return render (request, 'profiles/extra_profile.html', context)
But when I fill this form I got the error:
duplicate key value violates unique constraint "profiles_userprofile_user_id_key" DETAIL: Key (user_id)=(23) already exists.
On the traceback i see that error in this line user_profile.save(). As i understand it happens because this script create new item with same id but not update it. What i have to change on my code to update existing item but not to create. Thank you.
You need to use get_or_create, which will return an existing item if it was found; otherwise create a new instance of the model.
In your user profile update view:
user_profile, created = UserProfile.objects.get_or_create(user=profile.user)
Now, instead of creating a new profile each time - you are updating if a profile for that user already exists.

Categories

Resources