Convert Django ModelForm to use User object - python

I'm creating a registration form for my site.
I'm going with just the standard field entries of username, email, password in my User object.
The database tables are already created.
Models.py
class RegistrationForm(ModelForm):
class Meta:
model = User
views.py
def Registration(request):
RegForm = RegistrationForm(request.POST or None)
if request.method == 'POST':
if RegForm.is_valid():
newUser = User.objects.create_user(username, email, password)
RegForm.save()
try:
return HttpResponseRedirect('/Newuser/?userNm=' + clearUserName)
except:
raise ValidationError(('Invalid request'), code='300') ## [ TODO ]: add a custom error page here.
My question is, how do I represent these fields:
username, email, password
in this line (from the view):
newUser = User.objects.create_user(username, email, password)
that are part of the RegistrationForm() in models.py ?
Additionally: How do I properly represent this default model (User) in the modelform in forms.py?

To answer your second question:
Use UserCreationForm to represent registration process for User class, it handles validation and saving for you and you can always extend it (i.e. to add email field)
User class has a lot of related forms, depending of the use case: authentication, registration, password change etc. read the docs on authentication to find more.
https://docs.djangoproject.com/en/1.5/topics/auth/default/#module-django.contrib.auth.forms

You can get the validated data from the form using cleaned_date:
username = form.cleaned_data['username']
email = form.cleaned_data['email']
password = form.cleaned_data['password']
newUser = User.objects.create_user(username, email, password)
Now I think your approach has serious problems since you are saving the form again after creating the user object, also the User object expects the password field to be a hash of the password and some meta data, etc.
I highly recommend you use django-registration (or at least have a look at it) which handles all this stuff for you in addition to email validation, all you need is to provide the templates(and also you can find some templates for it like this )

Related

Passing Objects to Custom Validator in WTForms

Edit (trying to clarify the problem):
The custom validator I created for my flask app to check for duplicate usernames and emails does not work for admin users to modify other user accounts. When an admin user edits another user's username or email, the validator indicates it is a duplicate because it is trying to compare the current (admin) user's username and email, not the username and email of the user being edited. How do I write a custom validator to use the user object passed to the form in the view function below to compare username and email for the user being edited and not the logged in user?
Original post:
I am working on a Flask app that includes the ability for users with admin privileges to modify user account information. Currently, I am using Flask-WTF and WTForms to create a form on a user profile page that the account owner or a user with admin privileges (through role assignment in the user model) can edit user information. In the view function, I pass the user information to the form to pre-populate the form fields with the user information. When I made the profile editing form for the account owner, when the form is submitted I have a custom validator that compares the submitted data to the data for the currently logged in user to detect if there were any fields edited if then check if some of those fields (like username, email, etc.) are already in the database to avoid duplicates. For the form that admin users can edit other users, this validator keeps preventing writing new values to the database because the validator is comparing the logged in user information to the database and not the user information belonging to the account that is being edited. I want to know how to pass the user object that I use to populate the form fields to the form validation when the form is submitted? The Flask app is using a blueprint structure. I am pretty new to this so I hope the question makes sense. Here is some code that I think is helpful.
Here is the view function:
# Edit account view function for admin users
#users.route('/edit-account/<int:user_id>', methods=['GET', 'POST'])
#login_required
def edit_account(user_id):
# Get the info for the user being edited
user = User.query.get_or_404(user_id)
# Check to be sure user has admin privileges
if current_user.role != 'admin' and current_user.role != 'agent':
abort(403)
form = AccountEditForm()
if form.validate_on_submit():
# Update the profile picture, if a file is submitted
if form.profile_pic.data:
picture_file = save_picture(form.profile_pic.data)
user.profile_pic = picture_file
# Update the database entries for the user
user.username = form.username.data
user.first_name = form.first_name.data
user.last_name = form.last_name.data
user.email = form.email.data
user.role = form.role.data
db.session.commit()
flash(f'The account for {user.first_name} {user.last_name} has been updated.', 'success')
return redirect(url_for('users.users_admin'))
# Pre-populate the form with existing data
elif request.method == 'GET':
form.username.data = user.username
form.first_name.data = user.first_name
form.last_name.data = user.last_name
form.email.data = user.email
form.role.data = user.role
image_file = url_for('static', filename=f'profile_pics/{user.profile_pic}')
return render_template('account.html', title='account',
image_file=image_file, form=form, user=user)
Here is the form class that doesn’t work for the validator when an admin user is trying to edit another user’s account:
class AccountEditForm(FlaskForm):
username = StringField('Username',
validators=[DataRequired(), Length(min=2, max=20)])
first_name = StringField('First Name',
validators=[DataRequired(), Length(min=2, max=32)])
last_name = StringField('Last Name',
validators=[DataRequired(), Length(min=2, max=32)])
email = StringField('Email',
validators=[DataRequired(), Email()])
profile_pic = FileField('Update Profile Picture',
validators=[FileAllowed(['jpg', 'png'])])
role = SelectField('Role', validators=[DataRequired()],
choices=[('admin', 'Admin'), ('agent', 'Agent'), ('faculty', 'Faculty'),
('staff', 'Staff'), ('student', 'Student')])
submit = SubmitField('Update')
# Custom validation to check for duplicate usernames
def validate_username(self, username):
# Check to see if the form data is different than the current db entry
if username.data != username:
# Query db for existing username matching the one submitted on the form
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('Username is taken, please choose another.')
# Custom validation to check for duplicate email
def validate_email(self, email):
# Check to see if the form data is different than the current db entry
if email.data != user.email:
# Query db for existing email matching the one submitted on the form
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('Email is already used, please choose another or login.')
Here is the custom validator that works when the user is editing their own account from a different form class:
# Custom validation to check for duplicate usernames
def validate_username(self, username):
# Check to see if the form data is different than the current db entry
if username.data != current_user.username:
# Query db for existing username matching the one submitted on the form
user = User.query.filter_by(username=username.data).first()
if user:
raise ValidationError('Username is taken, please choose another.')
# Custom validation to check for duplicate email
def validate_email(self, email):
# Check to see if the form data is different than the current db entry
if email.data != current_user.email:
# Query db for existing email matching the one submitted on the form
user = User.query.filter_by(email=email.data).first()
if user:
raise ValidationError('Email is already used, please choose another or login.')
here is an example of passing object to the custom validator, and the scenario is exactly like yours:
class EditProfileAdminForm(EditProfileForm):
email = StringField('Email', validators=[DataRequired(), Length(1, 254), Email()])
role = SelectField('Role', coerce=int)
active = BooleanField('Active')
confirmed = BooleanField('Confirmed')
submit = SubmitField()
def __init__(self, user, *args, **kwargs): # accept the object
super(EditProfileAdminForm, self).__init__(*args, **kwargs)
self.role.choices = [(role.id, role.name)
for role in Role.query.order_by(Role.name).all()]
self.user = user # set the object as class attr
def validate_username(self, field):
# use self.user.username to get the user's username
if field.data != self.user.username and User.query.filter_by(username=field.data).first():
raise ValidationError('The username is already in use.')
def validate_email(self, field):
if field.data != self.user.email and User.query.filter_by(email=field.data.lower()).first():
raise ValidationError('The email is already in use.')
# view function
#admin_bp.route('/profile/<int:user_id>', methods=['GET', 'POST'])
#login_required
#admin_required
def edit_profile_admin(user_id):
user = User.query.get_or_404(user_id)
form = EditProfileAdminForm(user=user) # pass the object
# ...
Example codes come from a photo-sharing application.

How can i create custom users with only forms?

I am a beginner in Django an am using version 2.2 .I created a user form to sign a user in the site but it cant add other field information to the database
I have tried adding other fields in the fields list add adding fields but nothing works`
forms.py
from django import forms
from django.contrib.auth import (
authenticate,
get_user_model
)
User = get_user_model()
class UserRegisterForm(forms.ModelForm):
username = forms.CharField(label='PUsername')
email = forms.EmailField(label='Email address')
email2 = forms.EmailField(label='Confirm Email')
password = forms.CharField(widget=forms.PasswordInput,label='Password')
password2 = forms.CharField(widget=forms.PasswordInput,label='ConfirmPassword')
age = forms.CharField(label='your age')
info = forms.CharField(label='info about you')
class Meta:
model = User
fields = [
'username',
'email',
'email2',
'password',
'password2',
'age'
'info'
]
def clean(self, *args, **kwargs):
username = self.cleaned_data.get('email')
email = self.cleaned_data.get('email')
email2 = self.cleaned_data.get('email2')
password = self.cleaned_data.get('password')
password2 = self.cleaned_data.get('password2')
if email != email2:
raise forms.ValidationError("Emails must match")
email_qs = User.objects.filter(email=email)
if password != password2:
raise forms.ValidationError("Passwords must match")
email_qs = User.objects.filter(email=email)
if email_qs.exists():
raise forms.ValidationError(
"This email has already been registered")
username_ex = User.objects.filter(username=username)
if username_ex.exists():
raise forms.ValidationError("This username is taken")
return super(UserRegisterForm, self).clean(*args, **kwargs)
views.py
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate,get_user_model,login,logout
from .forms import CreateUserForms
import random
import string
def register_view(request):
if request.method=='POST':
frm=CreateUserForm(request.POST)
if frm.is_valid():
username, email, password = frm.cleaned_data['username'], frm.cleaned_data['email'], frm.cleaned_data['password1']
new_user = User.objects.create_user(username, email, password)
new_user.is_active = True # if you want to set active
new_user.save()
else:
frm=CreateUserForm()
return render(request,'registration/signup.html',{'form':frm})
def logout_view(request):
logout(request)
return redirect('/')
Expected results are the working form,but actual results are only saving the username, password and email
read the documentation on python super . you are using it in wrong way. calling super at last of the clean method mean it it execute the modelform clean method and code before super get modified .
The title of your question is
How can i create custom users with only forms?
The answer to that is: That's not possible. Whatever you want to save to the database, you have to have a corresponding model for it. Forms only determine what data is submitted by the clients and how that data is processed.
So if you want to save columns age and info for your users, you'll have to create a Model and the corresponding table and columns in your database to hold these values.
Now, the default Django User model only takes username, email and password (plus some booleans like is_staff, is_superuser, is_active etc...). If you want to save additional information, you have a few options that are well described in the official docs, either by extending the default User model or by substituting it.
The recommendation when starting a new Django project is to always at least substitute with your own User model (see this). If you will only ever have one type of user, then add the age and info fields on your custom User model. But if there's any chance that you might have different types of users where age and info might not be relevant in the future, extend your custom User model with a profile.

How to pass a username from a Django login form?

This will be an easy question for someone out there. I searched around quite a bit and if there's a thread that addresses this perfectly, please direct me and close this out. I'm building a very simple web form within our organization's website using Django.
In forms.py I've got a class for the login page:
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
if not fs.authenticateUser(user, pwd):
raise forms.ValidationError("Invalid password!")
I have to authenticate against our website's API, which is what the fs.authenticate bit is about; it just returns True or False. In views.py I do this:
if request.method == 'POST':
user_form = userLogin(data = request.POST)
if user_form.is_valid():
return redirect('PickupAuthorization/main')
else:
pass
The authentication and redirection to the main page works splendidly, and the login form throws an invalid password message as expected when the credential is incorrect. What I want to know is how to make "user" available to views.py. If I try to reference user_form.user in the view, Python tells me that user is not an attribute of userLogin!
I haven't done much with OOP, so I'm imagining that this has a simple answer, but I can't freakin' find it. Thanks!
EDIT: I have had to shelve this project for the moment in favor of more pressing matters, but I will update it when I have a solution that works properly.
You need to use authenticate method provided by Django in order to get the user. The authenticate method returns a user if it finds one.
To authenticate a given username and password, use authenticate(). It takes two keyword arguments, username and password, and it returns a User object if the password is valid for the given username. If the password is invalid, authenticate() returns None:
Generally the flow using authenticate goes like this:
forms.py
class userLogin(forms.Form):
user = forms.CharField(label = 'Username ', max_length = 25)
pwd = forms.CharField(label = 'Password ', widget = forms.PasswordInput)
def __init__(self, *args, **kwargs):
self.user_cache = None # You need to make the user as None initially
super(LoginForm, self).__init__(*args, **kwargs)
def clean(self):
cleaned_data = super(userLogin, self).clean()
user = cleaned_data.get("user")
pwd = cleaned_data.get("pwd")
# Now you get the user
self.user_cache = authenticate(username=user, password=pwd)
# Do other stuff
return self.cleaned_data
# Function to return user in views
def get_user(self):
return self.user_cache
Views.py
if request.method == 'POST':
user_form = userLogin(data = request.POST)
# You can now get user using the get_user method
user = user_form.get_user()
# Do other stuff
Another thing I'd like to add is if your user has already logged in you can simply do request.user.username in your views.py to get the current user.

Way to use email as the authentication field?

I am using django's inbuilt authentication system. Everything seems to be working fine. There are two fields that the user is requested to input at the time of signup: username and email. While logging in they are required to enter username and password.
I'd like to change this behavior so that username field is gone. I want to treat the email as the users username. So while signing in user will be required to put email / password
Is this possible while still using django's inbuilt auth system? I'm on django 1.7
Update
I had the need to add additional fields so I added the following to models.py
from django.db import models
from django.contrib.auth.models import User
class UserProfile(models.Model):
user = models.OneToOneField(User)
telephone_number = models.CharField(max_length=100)
website_url = models.CharField(max_length=100)
User.profile = property(lambda u: UserProfile.objects.get_or_create(user=u)[0])
The answer is directly in django documentation, in short words you should subclass AbstractBaseUser or AbstractUser (in second case you can't totally remove username field), create your own user manager based on BaseUserManager or UserManager and customize built-in auth forms if you're using it (or any app that you're using is using it).
This is not strictly cannon, but to avoid creating a new User class or Auth backend, I tend to let users log in with both username or email.
Anyways you'll want to ensure emails are unique as django does not check this by default.
You'll then have to override the default login view to support this. You can create something along the lines of:
class EmailUsernameLoginView(View):
def post(self, request):
next = request.POST.get('next', None)
username = request.POST.get('username', None)
password = request.POST.get('password', None)
error = ''
if username and password:
try:
usr = User.objects.get(email=username)
username = usr.username
except User.DoesNotExist:
pass # If the user doesn't exist, it's an username
user = authenticate(username=username, password=password)
if user is not None:
if user.is_active:
login(request, user)
return redirect(next)
else:
error = 'Your account is not active'
else:
error = 'The username / email - password comb is wrong'
else:
error = 'Please provide a username / email and password'
ctx = {'error': error} # Fill with needed feedback
return render(request, ctx, 'registration/login.html')
This is just a draft and you should probably include a form to help with validation / cleaning

How can I get the email field in the basic Django User model to be required?

I am trying to force users to enter their email when they sign up. I understand how to use form fields in general with ModelForms. I am unable to figure out how to force an existing field to be required, however.
I have the following ModelForm:
class RegistrationForm(UserCreationForm):
"""Provide a view for creating users with only the requisite fields."""
class Meta:
model = User
# Note that password is taken care of for us by auth's UserCreationForm.
fields = ('username', 'email')
I am using the following View to process my data. I am not sure how relevant it is, but it is worth saying that other fields (username, password) are properly loading with errors. The User model already has those fields set as required, however.
def register(request):
"""Use a RegistrationForm to render a form that can be used to register a
new user. If there is POST data, the user has tried to submit data.
Therefore, validate and either redirect (success) or reload with errors
(failure). Otherwise, load a blank creation form.
"""
if request.method == "POST":
form = RegistrationForm(request.POST)
if form.is_valid():
form.save()
# #NOTE This can go in once I'm using the messages framework.
# messages.info(request, "Thank you for registering! You are now logged in.")
new_user = authenticate(username=request.POST['username'],
password=request.POST['password1'])
login(request, new_user)
return HttpResponseRedirect(reverse('home'))
else:
form = RegistrationForm()
# By now, the form is either invalid, or a blank for is rendered. If
# invalid, the form will sent errors to render and the old POST data.
return render_to_response('registration/join.html', { 'form':form },
context_instance=RequestContext(request))
I have tried creating an email field in the RegistrationForm, but this seems to have no effect. Do I need to extend the User model and there override the email field? Are there any other options?
Thanks,
ParagonRG
Just override the __init__ to make the email field required:
class RegistrationForm(UserCreationForm):
"""Provide a view for creating users with only the requisite fields."""
class Meta:
model = User
# Note that password is taken care of for us by auth's UserCreationForm.
fields = ('username', 'email')
def __init__(self, *args, **kwargs):
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
This way, you don't have to completely re-define the field, but simply change the property. Hope that helps you out.

Categories

Resources