How to pass in user input into Custom User Model in Django - python

I have a custom user model that I need to add an extra field to. The problem I have is that the information needed is user input from the registration page.
This is the code from the form:
team = forms.ChoiceField(choices=teamChoices)
This is my user model:
class User(AbstractUser):
username = None
email = models.EmailField(_('email address'), unique=True)
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
objects = UserManager()
I need to add something like this:
groups = 'team'
But I don't know how I would get that information to my model. My user model is in models.py and my registration form is in forms.py.
This is is my current Register Form but I haven't tested to see if it works yet:
class RegisterForm(UserCreationForm):
first_name = forms.CharField(max_length=30,widget=forms.TextInput(attrs={'placeholder':'First Name', 'class': 'form-input'}))
last_name = forms.CharField(max_length=30,widget=forms.TextInput(attrs={'placeholder':'Last Name', 'class': 'form-input'}))
email = forms.EmailField(max_length=254, widget=forms.TextInput(attrs={'placeholder':'Email', 'class': 'form-input'}))
teamChoices = (('red','red'),('green','green'),('blue','blue'),('orange','orange'))
team = forms.ChoiceField(choices=teamChoices)
class Meta:
model = User
fields = ('first_name','last_name','email','team','password1','password2')

I ended up solving this issue and I wanted to post it here in case anyone's stuck like I was:
If you extended the UserCreationForm in your forms.py, you need to overload the def save(self, commit=True) method and you need to copy paste everything from the method from the source code into that overloaded method and then set your user.field value to your field. For my example, the line looks like this:
user.teams = self.cleaned_data['team']

Related

Hiding Email Address On UserUpdateForm

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.

Django multiple users, backend and generic views

I'm trying to learn auth django system and I have several questions.
I want to create multiple users with email authentication. So, I used AbtractBaseUser and AbtractBaseManager. I read django doc and this question on the website : Implementing multiple user types with Django 1.5
and I would like to use it. So, I try to implement the second point : all fields for my two user types are in 1 models and with user_type variable I can choose fields to show. My code is down below :
models.py
# -*- coding: utf-8 -*-
from django.db import models
from django.conf import settings
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)
TYPE_USER = (
('student', _('Student')),
('employee', _('Employee')),
)
class MyuserManager(BaseUserManager):
def create_user(self, email, last_name, first_name, date_of_birth, user_type, password=None):
if not email:
raise ValueError("users must have an email address")
user = self.model(
email = self.normalize_email(email),
last_name = last_name,
first_name = first_name,
date_of_birth = date_of_birth,
user_type = user_type,
)
user.set_password(password)
user.save()
return user
def create_superuser(self, email, password, last_name, first_name, date_of_birth, user_type):
user = self.create_user(email,
password = password,
date_of_birth = date_of_birth,
last_name = last_name,
first_name = first_name,
user_type = user_type,
)
user.is_admin = True
user.is_staff = True
user.save()
return user
class MyUser(AbstractBaseUser):
"""Based model user"""
email = models.EmailField(
verbose_name = 'email',
max_length=255,
unique=True,
)
last_name = models.CharField(max_length=30)
first_name = models.CharField(max_length=30)
date_of_birth = models.DateField()
user_type = models.CharField(_('Type'), choices=TYPE_USER, max_length=20, default='student')
phone = models.CharField(max_length=20, blank=True)
friends = models.ManyToManyField("self", blank=True)
faculty = models.ForeignKey(Faculty, null=True, blank=True)
desk = models.CharField(max_length=30, blank=True)
campus = models.ForeignKey(Campus, null=True, blank=True)
job = models.ForeignKey(Function, null=True, blank=True)
cursus = models.ForeignKey(Cursus, null=True, blank=True)
year = models.IntegerField(null=True, blank=True)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
is_staff = models.BooleanField(default=False)
objects = MyuserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['last_name', 'first_name', 'date_of_birth', 'user_type']
Question : Do you think it's correct ? or should i create 1 class with common fields and 2 classes for distinct type of user ?
Next, i would like to use my own backend so i implement a backend.py file in my project
backend.py
from fbLike.models import MyUser
from django.contrib.auth.backends import ModelBackend
class EmailAuthBackend(ModelBackend):
def authenticate(self, email=None, password=None, **kwargs):
try:
user = MyUser.objects.get(email=email)
if user.check_password(password):
return user
except MyUser.DoesNotExist:
return None
def get_user(self, user_id):
try:
return MyUser.objects.get(pk=user_id)
except:
return None
For me, it seems that is a correct implementation.
Questions : But, how to proceed with multi-table inheritence ? is it possible ? how to know what is the type of user before check password if i have for example :
class BaseUser(AbstractBaseUser):
email = models.EmailField(max_length=254, unique=True)
# common fields
class Student(BaseUser):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
# ...
class employee(BaseUser):
company_name = models.CharField(max_length=100)
# ...
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users.
Below my files :
forms.py
class StudentProfileForm(forms.ModelForm):
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
cursus = forms.ModelChoiceField(Cursus.objects.all())
year = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'cursus', 'year'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
class EmployeeProfileForm(forms.ModelForm):
date_of_birth = forms.DateField(widget = AdminDateWidget)
phone = forms.CharField(required=True)
faculty = forms.ModelChoiceField(Faculty.objects.all())
campus = forms.ModelChoiceField(Campus.objects.all())
job = forms.ModelChoiceField(Function.objects.all())
desk = forms.IntegerField(required=True)
class Meta:
model = MyUser
fields = ('email', 'password', 'first_name', 'last_name',
'date_of_birth', 'phone', 'faculty', 'campus', 'job', 'desk'
)
widgets = {
'password': forms.PasswordInput(render_value=True),
}
Here, I think it's too complex but i don't know how to reduce complexity.
views.py
class RegisterProfile(CreateView):
model = MyUser
template_name = 'fbLike/user_profile.html'
form_class = StudentProfileForm
second_form_class = EmployeeProfileForm
def get_object(self):
return super(RegisterProfile, self).get_object()
def get_success_url(self):
return reverse('login',)
def get_context_data(self, **kwargs):
context = super(RegisterProfile, self).get_context_data(**kwargs)
if 'studenForm' not in context:
context['studentForm'] = self.form_class
if 'employeeForm' not in context:
context['employeeForm'] = self.second_form_class
return context
def form_valid(self, form):
self.object = MyUser.objects.create_user(**form)
return super(RegisterProfile, self).form_valid(form)
def form_invalid(self, form):
return super(RegisterProfile, self).form_invalid(form)
def post(self, request, *args, **kwargs):
self.object = None
if request.POST['profileType'] == 'student':
form = self.form_class(request.POST, prefix='st')
form.user_type = 'student'
else:
form = self.second_form_class(request.POST, prefix='em')
form.user_type = 'employee'
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
class UpdateProfile(UpdateView):
model = MyUser
template_name = 'fbLike/modify_profile.html'
def get_form_class(self):
if self.request.user.user_type == 'student':
return StudentProfileForm
return EmployeeProfileForm
def get_success_url(self):
return reverse('welcome',)
For CreateProfile, I would like to send 2 forms in my template and thanks to javscript, i can choose the correct form. But when i submit the form, i have an error with the user password. When i check my users in the admin page, the hashpassword system seems to fail.
So, my first attempt to solve : it was to override save method in my form to use the request.user.set_password but it doesn't work.
I know it's a long explanation. But everyone could give me an example of CreateView class with 2 forms please ? If it isn't possible to use a class but a function how to implement this function ?
I thank you in advance
I will attempt to answer your multiple questions, but please in the future - one question per post. I realize you are new, but StackOverflow is not a discussion forum, have a look at the help section (link at the bottom of each page) specifically the section on asking questions.
I want to create multiple users with email authentication. [...] Do
you think it's correct ? or should i create 1 class with common fields
and 2 classes for distinct type of user ?
Its correct if it works with you. These kinds of questions are better suited for codereview.stackexchange.com. Especially as there isn't a bug.
But, how to proceed with multi-table inheritence ? is it possible ?
how to know what is the type of user before check password if i have
for example [...]
First, authentication backends are different topic altogether; they don't have anything to do with multiple table inheritance (I think you mean relationships or multiple user profiles).
You only need multiple authentication backends if your password authentication algorithm is different for each user type. If all user passwords are stored in the same location/system/database, then you don't need multiple authentication backends.
You should implement a method in your base user class, that returns the type of user. You can then use user_passes_check decorator to restrict access in your views.
This way you will achieve what I think is what you are after "this section only for students", etc.
Finally, with my models.py, backend.py i would try to use the CRUD system in my views.py to create and update users [..] Here, I think it's too complex but i don't know how to reduce
complexity.
You don't need to repeat all the fields of your model in your ModelForm, you only need to override fields if you need to modify them, or to add extra "non-model" fields to the form. You can start with the simple ModelForm, like this:
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'password')
A common use of adding an additional field is to ask for password confirmation, by having an extra password field.
For CreateProfile, I would like to send 2 forms in my template and
thanks to javscript, i can choose the correct form. But when i submit
the form, i have an error with the user password. When i check my
users in the admin page, the hashpassword system seems to fail. So, my
first attempt to solve : it was to override save method in my form to
use the request.user.set_password but it doesn't work.
Use a form wizard to implement a multi-step registration process. The first step, you ask the basic information and account type to be created; on the second step, you only send the relevant form instead of sending two forms and then switching them around using javascript.
I don't see anywhere in your code where you use set_password, so can't comment on the second part.

Django, Update Profile, exclude current user email

I have created an Update Profile page. So the fields are: Email, First Name, Last Name.
In the validation, I'm trying to exclude logged user's email address, and also I'm filtering other user's email addresses. I think if you see the code you will understand what I'm talking about.
I read several questions here but couldn't find something. Some users had the same issue.
So with the code below, the issue I'm getting is:
type object 'User' has no attribute 'email'.
I tried many ways to get the current user's email address(before I save the form) but still nothing.
forms.py
class UpdateProfile(forms.ModelForm):
email = forms.EmailField(required=True)
first_name = forms.CharField(required=False)
last_name = forms.CharField(required=False)
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
def clean_email(self):
email = self.cleaned_data.get('email')
current_user_email = User.email
if User.objects.filter(email__iexact=email).exclude(email__iexact=current_user_email).count() > 0:
raise forms.ValidationError('This email address is already in use.'
'Please supply a different email address.')
return email
def save(self, commit=True):
user = super(UpdateProfile, self).save(commit=False)
if commit:
user.save()
return user
I suppose that in line current_user_email = User.email "User" isn't really a actual user instance, it is a model class which you imported for setting model = User in Meta.
If you want to use this form only for editing user data You should do something like this:
urls.py
urlpatterns = patterns('project.apps.app_name.views',
url(r'^edit-user/(?P<pk>\d+)/?$','edit_user',name='edit_user'),
)
app_name/views.py:
def edit_user(request,pk):
user_instance = User.objects.get(pk=pk)
form = UpdateProfile(request.POST, instance=user_instance)
.... more code ....
And in forms.py you should change line from:
current_user_email = User.email
to:
current_user_email = self.instance.email
But the proper way to do that (if you use django >1.6) would be to create custom User model, and set email field attribute unique=True.

How can I prefill value in a custom form field in a ModelForm

Let's say I have a model as follows.
models.py
class Profile(models.Model):
user = models.OneToOneField(User)
middle_name = models.CharField(max_length=30, blank=True, null=True)
And I have a custom field email as follows in a ModelForm
forms.py
class ProfileForm(ModelForm):
email = forms.CharField()
class Meta:
model = models.Profile
fields = ('email', 'middle_name')
In the am setting an instance of the above mentioned modelform so the data is prefilled in the form for an edit template as follows.
views.py
def edit_profile(request):
profile = models.Profile.objects.get(user=request.user)
profileform = forms.ProfileForm(instance=profile)
return render_to_response('edit.html', { 'form' : 'profileform' }, context_instance=RequestContext(request))
Now in the form I get all the values prefilled for all the fields under Profile model but the custom fields are empty and it makes sense.
but is there a way I can prefill the value of the custom fields ? maybe something like:
email = forms.CharField(value = models.Profile.user.email)
Can I suggest something else? I'm not a huge fan of having that email field within a ModelForm of Profile if it has nothing to do with that model.
Instead, how about just having two forms and passing in initial data to your custom one containing email? So things would look like this:
forms.py
# this name may not fit your needs if you have more fields, but you get the idea
class UserEmailForm(forms.Form):
email = forms.CharField()
views.py
profile = models.Profile.objects.get(user=request.user)
profileform = forms.ProfileForm(instance=profile)
user_emailform = forms.UserEmailForm(initial={'email': profile.user.email})
Then, you're validating both the profile and user email form, but otherwise things are mostly the same.
I assume you are not sharing logic between the Profile ModelForm and this UserEmailForm. If you need profile instance data, you could always pass that in there.
I prefer this approach because it's less magical and if you look back at your code in a year, you won't be wondering why, in brief scanning, why email is part of the ModelForm when it does not exist as a field on that model.

Saving Django ModelForms with Multiple Inheritance

I have a web application that allows users to create an account, and in doing so creates a user object form a the standard Django User model, associated with a custom UserProfile model, as well as an Address model. I have built an HTML form that allows the user to update their address, and profile, by means of using a ContactInfoForm that subclasses both the AddressForm and UserProfileForm; both of which are ModelForms, as follows:
class AddressForm(forms.ModelForm):
class Meta:
model = common_models.Address
exclude = ('updated_dt','address_type','created_dt')
def __init__(self,*args,**kwargs):
super(AddressForm,self).__init__(*args,**kwargs)
firstname = forms.CharField(max_length=100, min_length=1, error_messages={'required':'Please Enter First Name'})
lastname = forms.CharField(max_length=100, min_length=1, error_messages={'required':'Please Enter Last Name'})
address1 = forms.CharField(max_length=100, min_length=1, error_messages={'required':'Please Enter Address'})
etc...
class UserProfileForm(forms.ModelForm):
class Meta:
model = common_models.UserProfile
exclude = ('created_dt','updated_dt','entity_active','profile_hash','user','address')
account_type = forms.ChoiceField(choices=account_choices,widget=forms.Select(attrs={'id':'account_type_list'}),error_messages={'required':'Please Select Account Type'})
name = forms.CharField(max_length=100, min_length=1, error_messages={'required':'Please Company Name'})
supplier_type = forms.ModelChoiceField(queryset=common_models.SupplierTypeCode.objects.all(),required=False,widget=forms.Select(attrs={'id':'account_type_select'}))
buyer_type = forms.ModelChoiceField(queryset=common_models.ClientTypeCode.objects.all(),widget=forms.Select(attrs={'id':'account_type_select'}),required=False)
class ContactInfoForm(AddressForm,UserProfileForm):
class Meta:
model = common_models.User
exclude = ('email','username',
'password','last_login','date_joined')
def __init__(self,user=None,request_post_data=None,*args,**kwargs):
if not request_post_data:
params = dict([tuple([k,v]) for k,v in user.get_profile().address.__dict__.items()] +
[tuple([k,v]) for k,v in user.get_profile().__dict__.items()])
super(ContactInfoForm,self).__init__(initial=params,*args,**kwargs)
else:
super(ContactInfoForm,self).__init__(request_post_data,instance=user)
Now, I have the following questions:
1) How do I save the ContactInfoForm, such that both the user_profile and the address tables are updated, along with the auth_user table? I have tried overriding the save function in the ContactInfoForm, then calling the save function of Address and UserProfile as follows:
def save(self):
address = AddressForm.save(self)
profile = UserProfileForm.save(self)
however, that doesn't work as the instance of self is a user object, and thus both the above functions return a user object
2) Is my implementation of the init method of the ContactInfoForm the best way to pre-populate the HTML form when the user first visits the update contact info page? In other words, is the construction of the params dictionary and using it as the initial argument correct. Keep in my mind, I have access to the user object from request.user since this view is behind a login_required decorator...
3) Is there perhaps a better way to achieve what I am trying to achieve that isn't as complicated and more Django/Pythonic?
Usually in Django such thing is being made by creating 3 separate forms and process them all in one view.
address_form = AddressForm(request.POST)
profile_form = UserProfileForm(request.POST)
contacts_form = ContactInfoForm(request.POST)
if address_form.is_valid() and profile_form.is_valid() and contacts_form.is_valid():
address_form.save()
profile_form.save()
contacts_form.save()
Maybe it's bit more code this way but it's much more clear and easy to read.

Categories

Resources