Test UpdateView for useraccounts application - django

Test doesn't give status_code 302 in user profile UpdateView and so there's no updates occurs on the object
the model code
class User(AbstractBaseUser, PermissionsMixin):
'''
This a replaced user profile instead of the default django one
'''
language_choices=[('en',_('English')),('se',_('Swedish'))]
email=models.CharField(verbose_name=_('Email'), max_length=128, blank=False, unique=True)
first_name=models.CharField(verbose_name=_('First Name'), max_length=128)
last_name=models.CharField(verbose_name=_('Last Name'), max_length=128)
joined_at=models.DateField(
verbose_name=_('Joined at'),
auto_now_add=True,
blank=False
)
language=models.CharField(
verbose_name=_('Language'),
max_length=2,
choices=language_choices,
default=language_choices[0][0]
)
active=models.BooleanField(verbose_name=_('Active'), default=False)
is_superuser=models.BooleanField(verbose_name=_('Is Superuser'), default=False)
is_active=models.BooleanField(verbose_name=_('Is Active'), default=True)
is_staff=models.BooleanField(verbose_name=_('Is Staff'), default=False)
The form code
class EditUserForm(UserChangeForm):
'''
Profile form to update existing user information
'''
# error message for email matches
error_messages = {
'email_mismatch': _("The two email fields didn't match."),
}
# create field for email
email1 = forms.EmailField(
label=_("Email"),
widget=forms.EmailInput,
help_text=_("If you change your email your account will be inactive untill your reactivate by email link."),
)
# get the email from confirmed email field
email2 = forms.EmailField(
label=_("Confirm Email"),
widget=forms.EmailInput,
help_text=_("Enter the same email as before, for verification."),
)
# hide password field
password = ReadOnlyPasswordHashField(label="Password")
class Meta:
'''
Initial fields and model for the form
'''
model = models.User
fields = ('first_name','last_name','email1','email2', 'language')
def clean_email2(self):
'''
Method for if email and confirmed email are the same
This method works when confirmed email cleared
'''
# get the email from email field
email1 = self.cleaned_data.get("email1")
# get the email from confirmed email field
email2 = self.cleaned_data.get("email2")
# check if both emails are equal
if email1 and email2 and BaseUserManager.normalize_email(email1) != BaseUserManager.normalize_email(email2):
# give an error message if emails not matches
raise forms.ValidationError(
self.error_messages['email_mismatch'],
code='email_mismatch')
# return the confirmed email
return BaseUserManager.normalize_email(email2)
def save(self, commit=True):
'''
Method tosave the edited user data
'''
# get the initial method
user = super().save(commit=False)
# set the email on the model field
user.email = self.cleaned_data["email1"]
# save edited user data
if commit:
user.save()
return user
def __init__(self, *args, **kwargs):
'''
Method for initial values and functions for the SignUp form class
'''
# get user data from User model
user = get_user_model().objects.get(email=kwargs['instance'])
# get the initial form class values
super(EditUserForm, self).__init__(*args, **kwargs)
# Add the current email as the inital email
self.fields['email1'].initial = user.email
# Add the current email as the intial confirmed email
self.fields['email2'].initial = user.email
# Add help text in the password field for change
self.fields['password'].help_text=(
_("Raw passwords are not stored, so there is no way to see "
"this user's password, but you can change the password "
"using this form.")
.format(reverse(
'core:ChangePassword',
kwargs={'pk':user.pk})))
and the view code
class EditUser(UserPassesTestMixin, UpdateView):
'''
Class view to update user details
'''
# used template
template_name = 'core/edit.html'
# View model
model = models.User
# View form
form_class = forms.EditUserForm
def test_func(self):
return self.request.user == get_user_model().objects.get(pk=self.kwargs['pk'])
def get_success_url(self):
'''
Metho to redirect after a valid form
'''
# check if the email is verified
if self.request.user.active:
# get the user key
pk=self.request.user.pk
# redirect to profile details
return reverse_lazy('core:details', kwargs={'pk':pk})
else:
# send a verification email
return SendActivationEmail(self.request, self.request.user)
the test code
self.viewuser_url = reverse('core:details', kwargs={'pk':self.user.pk})
self.edituser_url = reverse('core:edit', kwargs={'pk':self.user.pk})
def test_edit_user_post(self):
first_name = 'Osama'
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
self.assertRedirects(response, self.viewuser_url)
self.user.refresh_from_db()
self.assertEqual(self.user.first_name, first_name)
I tried to get assertEqual for the status code and it gitves me 200 instead of 302
also I tried to enter the form details instead of model details and it gives me an error
The get test works fine and also permission test works great.. all the models, forms and urls test works perfect.
I don't know how I can test this..

If the form isn't valid, then the form will be re-rendered with errors and you'll get a 200 response.
To debug the problem, check response.context['form'].errors in your test to see what the problem is.
response = self.client.post(self.edituser_url,
data={
'first_name': first_name,
'last_name': self.last_name,
'email': self.email,
})
print(response.context['form'].errors
Your view uses EditUserForm, but you are not posting any values for email1 or email2, so there is probably something in the errors about missing data.

Related

Can `clean` and `clean_fieldname` methods be used together in Django ModelForm?

I am trying to implement registration with email and phone on a website. A user can register with either phone or email or both. If a user keeps both phone and email field empty, a ValidationError is raised, "You cannot leave both phone and email fields blank. You must fill at least one of the fields."
We have separate clean methods for username, email, phone, password. I do not want to implement the above-mentioned validation on save(). I don't want to define a clean method in the User model, either.
I have written tests for this form, and they pass. But what errors could possibly arise if I use both clean and clean_fieldname together? Could it become a problem when working with views?
I have 3 questions:
Can I use both clean_fieldname and clean methods in a
form?
In what other way can I make sure that user registers with at least
phone or email?
How do clean() and validate() works? I have read django documentation, but I don't understand it completely.
Here's the code I implemented.
class RegisterForm(SanitizeFieldsForm, forms.ModelForm):
email = forms.EmailField(required=False)
message = _("Phone must have format: +9999999999. Upto 15 digits allowed."
" Do not include hyphen or blank spaces in between, at the"
" beginning or at the end.")
phone = forms.RegexField(regex=r'^\+(?:[0-9]?){6,14}[0-9]$',
error_messages={'invalid': message},
required=False)
password = forms.CharField(widget=forms.PasswordInput())
MIN_LENGTH = 10
class Meta:
model = User
fields = ['username', 'email', 'phone', 'password',
'full_name']
class Media:
js = ('js/sanitize.js', )
def clean(self):
super(RegisterForm, self).clean()
email = self.data.get('email')
phone = self.data.get('phone')
if (not phone) and (not email):
raise forms.ValidationError(
_("You cannot leave both phone and email empty."
" Signup with either phone or email or both."))
def clean_username(self):
username = self.data.get('username')
check_username_case_insensitive(username)
if username.lower() in settings.CADASTA_INVALID_ENTITY_NAMES:
raise forms.ValidationError(
_("Username cannot be “add” or “new”."))
return username
def clean_password(self):
password = self.data.get('password')
validate_password(password)
errors = []
email = self.data.get('email')
if email:
email = email.split('#')
if email[0].casefold() in password.casefold():
errors.append(_("Passwords cannot contain your email."))
username = self.data.get('username')
if len(username) and username.casefold() in password.casefold():
errors.append(
_("The password is too similar to the username."))
phone = self.data.get('phone')
if phone:
if phone_validator(phone):
phone = str(parse_phone(phone).national_number)
if phone in password:
errors.append(_("Passwords cannot contain your phone."))
if errors:
raise forms.ValidationError(errors)
return password
def clean_email(self):
email = self.data.get('email')
if email:
if User.objects.filter(email=email).exists():
raise forms.ValidationError(
_("Another user with this email already exists"))
return email
def clean_phone(self):
phone = self.data.get('phone')
if phone:
if User.objects.filter(phone=phone).exists():
raise forms.ValidationError(
_("Another user with this phone already exists"))
return phone
def save(self, *args, **kwargs):
user = super().save(*args, **kwargs)
user.set_password(self.cleaned_data['password'])
user.save()
return user
You can get a lot out of reading the Django code; it is a well-commented codebase! The relevant section is in django/forms/forms.py. When a form is cleaned/validated, it will call full_clean. This will first call _clean_fields, which calls the field clean and looks for a clean_{fieldname} method on the form to call. Then, the form clean is called.
def full_clean(self):
"""
Clean all of self.data and populate self._errors and self.cleaned_data.
"""
self._errors = ErrorDict()
if not self.is_bound: # Stop further processing.
return
self.cleaned_data = {}
# If the form is permitted to be empty, and none of the form data has
# changed from the initial data, short circuit any validation.
if self.empty_permitted and not self.has_changed():
return
self._clean_fields()
self._clean_form()
self._post_clean()
def _clean_fields(self):
for name, field in self.fields.items():
# value_from_datadict() gets the data from the data dictionaries.
# Each widget type knows how to retrieve its own data, because some
# widgets split data over several HTML fields.
if field.disabled:
value = self.get_initial_for_field(field, name)
else:
value = field.widget.value_from_datadict(self.data, self.files, self.add_prefix(name))
try:
if isinstance(field, FileField):
initial = self.get_initial_for_field(field, name)
value = field.clean(value, initial)
else:
value = field.clean(value)
self.cleaned_data[name] = value
if hasattr(self, 'clean_%s' % name):
value = getattr(self, 'clean_%s' % name)()
self.cleaned_data[name] = value
except ValidationError as e:
self.add_error(name, e)
def _clean_form(self):
try:
cleaned_data = self.clean()
except ValidationError as e:
self.add_error(None, e)
else:
if cleaned_data is not None:
self.cleaned_data = cleaned_data

How to add additional user information while the user is logged in?

I have created a login in page and now want to redirect the user to a page to add additional user information. How do I ensure that the user is "logged in" and that the information is added to that user's profiles? I know I need to use something like user=User.objects.get(email=email) to identify the user but I don't want to have the user enter their email on every single page to identify themselves. Is there something I can pass to keep the user Logged-in?
Here is the view:
def add_user_profile(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = AddUserProfileForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/summary/')
# if a GET (or any other method) we'll create a blank form
else:
form = AddUserProfileForm()
return render(request, 'registration/add_user_profile.html', {'form': form})
Here is the form:
class AddUserProfileForm(forms.ModelForm):
this_year = datetime.date.today().year
Years = range(this_year-70, this_year+1)
required_css_class = 'required'
first_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'First Name'}), label="")
middle_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Middle Name'}), label="")
last_name = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Last Name'}), label="")
date_of_birth = forms.DateField(widget=forms.extras.widgets.SelectDateWidget(years=Years))
Primary_address = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Primary address'}), label="")
Primary_address_zipcode = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Zip Code'}), label="")
class Meta:
model = User
fields = ('first_name', 'middle_name', 'last_name', 'date_of_birth', 'Primary_address', 'Primary_address_zipcode')
Here is the url.py
url(r'^add_user_profile/$',
views.add_user_profile,
name='add_user_profile'),
You just need to create a view which will deal with request.user. If your update_profile functionality is rather simple you may take a look at UpdateView. You just need to overwrite get_object() to look like this:
def get_object(self, queryset=None):
return self.request.user
After this you just need to set up you UpdateView and create a proper template. You can read about this in the docs.

Django permissions outside of the admin and in a custom form

I have an app with 2 user levels; Superuser, and Staff. Superuser will login to this app and create a new user and assign the user either Superuser or Staff permissions, but they are not logging in through the default Django admin, they will be logging into a custom admin. How do I display the permission checkboxes in my form and how do I save them for that new user being created? Is it merely just a normal checkbox or is there something I need to override to be able to accomplish this?
Here is my CreateUserForm as it is now.
class CreateUserForm(forms.Form):
username = forms.EmailField(max_length=50)
email = forms.EmailField()
first_name = forms.CharField(max_length=150)
last_name = forms.CharField(max_length=150)
password1 = forms.CharField(max_length=30, widget=forms.PasswordInput(render_value=False), label='Password')
password2 = forms.CharField(max_length=30, widget=forms.PasswordInput(render_value=False), label='Password Confirmation')
address_1 = forms.CharField(max_length=50)
address_2 = forms.CharField(max_length=50)
city = forms.CharField(max_length=50)
province = forms.CharField(max_length=2)
country = forms.CharField(max_length=50)
postal_code = forms.CharField(max_length=10)
work_phone = forms.CharField(max_length=20)
mobile_phone = forms.CharField(max_length=20)
fax = forms.CharField(max_length=20)
url = forms.CharField()
comments = forms.CharField(widget=forms.Textarea)
def clean_username(self):
try:
User.objects.get(username=self.cleaned_data['username'])
except User.DoesNotExist:
return self.cleaned_data['username']
raise forms.ValidationError("Sorry, this username has already been taken. Please choose another.")
def clean_email(self):
try:
User.objects.get(email=self.cleaned_data['email'])
except User.DoesNotExist:
return self.cleaned_data['email']
raise forms.ValidationError("Sorry, this email has already been taken. Please choose another.")
def clean(self):
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:
if self.cleaned_data['password1'] != self.cleaned_data['password2']:
raise forms.ValidationError("You must type the same password each time.")
if ' ' in self.cleaned_data['username']:
raise forms.ValidationError("username must not contain spaces")
return self.cleaned_data
def save(self):
new_user = User.objects.create_user(
username = self.cleaned_data['username'],
email = self.cleaned_data['email']
)
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.set_password(self.cleaned_data['password'])
new_user.is_active = True
new_user.save()
new_profile = UserProfile(
# TODO:
)
Thanks
If you mean the permission checkboxes for is_staff and is_superuser, then you're probably best off using a ModelForm w/ the User model. This will automatically take care of everything for you.
class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = ('last_login', 'date_joined')
EDIT per OP update: You can just add 2 forms.BooleanField() fields to your form for is_superadmin and is_staff (or name them differently), and much like you're already doing in the save method you can do new_user.is_staff = self.cleaned_data['is_staff']. You may consider using a choice field instead, w/ a dropdown w/ 3 entries, "Normal User", "Staff User", "Admin User", and then set is_staff and is_superadmin according to the user's selection.
It's also still possible to use a ModelForm on the User model and just add the necessary extra fields in addition. It may or may not be worth it to you, depending how custom you're getting.
Another note about your form - when it comes to editing existing users, this won't be able to be used for that w/ the current clean methods on username/email. But if you tweak those to exclude the current instance (if set) from the lookup, you will be able to use this form for editing existing users (though since it's not a model form you'd need to populate the initial data manually).

Creating an Add user form in Django

I want to create a SINGLE form which gives the ability to the admin to create a new user with extended profile. Please note that, I don't want to use admin and registration apps.
I have extended the user with the UserProfile model. I have read all the documents related to extending user profile. But, I really don't know how to save these information.
I coded the following django form for this issue:
class CreateUserForm(forms.Form):
username = forms.CharField(max_length=30)
first_name = forms.CharField()
last_name = forms.CharField()
password1=forms.CharField(max_length=30,widget=forms.PasswordInput()) #render_value=False
password2=forms.CharField(max_length=30,widget=forms.PasswordInput())
email=forms.EmailField(required=False)
title = forms.ChoiceField(choices=TITLE_CHOICES)
def clean_username(self): # check if username dos not exist before
try:
User.objects.get(username=self.cleaned_data['username']) #get user from user model
except User.DoesNotExist :
return self.cleaned_data['username']
raise forms.ValidationError("this user exist already")
def clean(self): # check if password 1 and password2 match each other
if 'password1' in self.cleaned_data and 'password2' in self.cleaned_data:#check if both pass first validation
if self.cleaned_data['password1'] != self.cleaned_data['password2']: # check if they match each other
raise forms.ValidationError("passwords dont match each other")
return self.cleaned_data
def save(self): # create new user
new_user=User.objects.create_user(username=self.cleaned_data['username'],
first_name=self.cleaned_data['first_name'],
last_name=self.cleaned_data['last_name'],
password=self.cleaned_data['password1'],
email=self.cleaned_data['email'],
)
return new_user
Is it OK? however it gives me an error in first_name and last_name. Says django doesn't expect first_name and last_name in save() method.
create_user only supports the username, email and password arguments. First call create_user, then add the extra values to the saved object.
new_user=User.objects.create_user(self.cleaned_data['username'],
self.cleaned_data['email'],
self.cleaned_data['password1'])
new_user.first_name = self.cleaned_data['first_name']
new_user.last_name = self.cleaned_data['last_name']
new_user.save()

Simple form not validating

I have found here on stackoverflow a method to extend django's built-in authentication using signals. My base User is defined by 'email' and passwords (so no username there). So I'm trying to modify it to my needs, but I'm geting a validation error for my form. Strange thing is that error is connected to the User.email field and I'm getting 'already in use' even though I'm just registering at the moment. Is it trying to save it 2 times or what ? I've discovered it when I was sending dictionary with data to form's contstructor in shell: form = MyForm(data={}). After this form was still invalid, but changing email to different value finally gave me True.
The user_created function connected to registration signal :
def user_created(sender, user, request, **kwargs):
form = CustomRegistrationForm(request.POST, request.FILES)
if form.is_valid():
data = UserProfile(user=user)
data.is_active = False
data.first_name = form.cleaned_data['first_name']
data.last_name = form.cleaned_data['last_name']
data.street = form.cleaned_data['street']
data.city = form.cleaned_data['city']
data.save()
else:
return render_to_response('user/data_operations/error.html', {'errors': form._errors}, context_instance=RequestContext(request))
user_registered.connect(user_created)
My form :
class CustomRegistrationForm(RegistrationForm):
first_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
last_name = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
street = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
city = forms.CharField(widget=forms.TextInput(attrs=attrs_dict), max_length=50)
My model :
class UserProfile(models.Model):
first_name = models.CharField(_("Name"), max_length=50, blank=False,)
last_name = models.CharField(_("Last name"), max_length=50, blank=False,)
street = models.CharField(_("Street"), max_length=50, blank=False,)
city = models.CharField(_("City"), max_length=50, blank=False,)
user = models.ForeignKey(User, unique=True, related_name='profile',)
Registration form :
class RegistrationForm(forms.Form):
email = forms.EmailField(widget=forms.TextInput(attrs=dict(attrs_dict,
maxlength=75)),
label=_("Adres email"))
password1 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo"))
password2 = forms.CharField(widget=forms.PasswordInput(attrs=attrs_dict, render_value=False),
label=_("Haslo powtorzone"))
def clean_email(self):
email = self.cleaned_data.get("email")
if email and User.objects.filter(email=email).count() > 0:
raise forms.ValidationError(
_(u"Already in use."))
return email
your 'user_registered' signal is sent after the User is saved. So it already has an 'email' field defined.
UPDATE
Using restless thinking :
form = CustomRegistrationForm(request.POST, request.FILES, notvalidateemail=True)
and in form :
def __init__(self, *args, **kwargs):
self.notvalidateemail = kwargs.pop('notvalidateemail',False)
super(CustomRegistrationForm, self).__init__(*args, **kwargs)
def clean_email(self):
if self.notvalidateemail:
return
else:
#your cleaning here
return email
Problem:
Your form is first saved by django-registration. Then you save it again in user_created.
Solutions:
Use a different form in user_created. One that won't have already saved fields (these from User model like email). You just want to save additional data in user_created, right?
Add some parameters to the form like:
in user_created:
form = CustomRegistrationForm(dontvalidateemail=True, request.POST, request.FILES)
and in form's init;
self.dontvalidateemail = dontvalidateemail
then just check it in clean_email.