Created
February 18, 2014 10:06
-
-
Save jacoor/9067973 to your computer and use it in GitHub Desktop.
basic django user account model + tests + admin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from django.contrib import admin | |
from django.contrib.auth.admin import UserAdmin | |
from django.utils.translation import ugettext_lazy as _ | |
from earnest.accounts.models import Account, AccessHistory | |
from earnest.accounts.forms import AdminPasswordChangeForm, AdminRegisterUserFullForm | |
class ReadonlyTabularInline(admin.TabularInline): | |
can_delete = False | |
extra = 0 | |
editable_fields = [] | |
def get_readonly_fields(self, request, obj=None): | |
fields = [] | |
for field in self.model._meta.get_all_field_names(): | |
if (not field == 'id'): | |
if (field not in self.editable_fields): | |
fields.append(field) | |
return fields | |
def has_add_permission(self, request): | |
return False | |
class AccessHistoryInline(ReadonlyTabularInline): | |
model = AccessHistory | |
editable_fields = [] | |
class AccountAdmin(UserAdmin): | |
list_display = ('email',) | |
ordering = ('email',) | |
inlines = [ | |
AccessHistoryInline, | |
] | |
readonly_fields = ('date_joined',) | |
change_password_form = AdminPasswordChangeForm | |
add_form = AdminRegisterUserFullForm | |
fieldsets = () | |
add_fieldsets = ( | |
(None, { | |
'classes': ('wide',), | |
'fields': AdminRegisterUserFullForm.Meta.fields, | |
} | |
), | |
) | |
#admin.site.disable_action('edit_permissions') | |
admin.site.register(Account, AccountAdmin) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from django.contrib.auth import get_user_model | |
from django.contrib.auth.backends import ModelBackend | |
class Emailbackend(ModelBackend): | |
def authenticate(self, email=None, password=None, *args, **kwargs): | |
if email is None: | |
if not 'username' in kwargs or kwargs['username'] is None: | |
return None | |
email = kwargs['username'] | |
email = email.lower() | |
user_model = get_user_model() | |
try: | |
user = user_model.objects.get(email=email, is_active=True) | |
except user_model.DoesNotExist: | |
return None | |
if user.check_password(password): | |
return user |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
import datetime | |
from django.utils.translation import ugettext_lazy as _ | |
from localflavor.us.forms import USZipCodeField | |
from django.db import models | |
class USZipModelField(models.CharField): | |
description = "CharField with USPostalCodeField widget" | |
def formfield(self, **kwargs): | |
# This is a fairly standard way to set up some defaults | |
# while letting the caller override them. | |
defaults = {'form_class': USZipCodeField} | |
defaults.update(kwargs) | |
return super(USZipModelField, self).formfield(**defaults) | |
from south.modelsinspector import add_introspection_rules | |
add_introspection_rules([], ["^earnest\.accounts\.fields\.USZipModelField"]) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from django.contrib.auth.forms import AdminPasswordChangeForm as DjagnoAdminPasswordChangeForm, \ | |
SetPasswordForm, PasswordChangeForm, \ | |
AuthenticationForm as DjangoAuthenticationForm | |
from django import forms | |
from django.utils.translation import ugettext_lazy as _ | |
from passwords.fields import PasswordField | |
from earnest.accounts.models import Account | |
class ValidatingSetPasswordForm(SetPasswordForm): | |
new_password1 = PasswordField(label=_("New password")) | |
new_password2 = PasswordField(label=_("New password confirm")) | |
class ValidatingPasswordChangeForm(PasswordChangeForm): | |
new_password1 = PasswordField(label=_("New password")) | |
new_password2 = PasswordField(label=_("New password confirmation")) | |
class AdminPasswordChangeForm(DjagnoAdminPasswordChangeForm): | |
password1 = PasswordField(label=_("New password")) | |
password2 = PasswordField(label=_("New password confirm")) | |
class RegisterUserFullForm(forms.ModelForm): | |
password = PasswordField(label=_("Password")) | |
password_confirm = PasswordField(label=_("Password confirmation")) | |
def __init__(self, *args, **kwargs): | |
super(RegisterUserFullForm, self).__init__(*args, **kwargs) | |
self.fields['agree_to_terms_conditions'].is_checkbox = True | |
self.fields['agree_to_terms_conditions'].required = True | |
self.fields['agree_to_terms_conditions'].initial = False | |
def clean_password_confirm(self): | |
password = self.cleaned_data.get('password') | |
password_confirm = self.cleaned_data.get('password_confirm') | |
if password != password_confirm: | |
raise forms.ValidationError(_("Passwords doesn't match.")) | |
return password_confirm | |
def save(self, commit=True): | |
account = super(RegisterUserFullForm, self).save(commit=False) | |
account.set_password(self.cleaned_data["password"]) | |
if commit: | |
account.save() | |
return account | |
class Meta: | |
model = Account | |
fields = ['email', 'gender', 'birth_year', 'password','password_confirm', 'zipcode', | |
'financial_institution', "agree_to_terms_conditions"] | |
class AdminRegisterUserFullForm(RegisterUserFullForm): | |
#small hack to show those fields | |
agree_to_terms_conditions = forms.BooleanField(required=False, initial=True) | |
def __init__(self, *args, **kwargs): | |
super(AdminRegisterUserFullForm, self).__init__(*args, **kwargs) | |
self.fields['agree_to_terms_conditions'].required = False | |
def clean(self): | |
cleaned_data = self.cleaned_data | |
cleaned_data['agree_to_terms_conditions'] = True | |
return cleaned_data |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from django.db import models | |
from django.utils.translation import ugettext_lazy as _ | |
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager | |
from earnest.accounts.fields import USZipModelField | |
class AccountManager(BaseUserManager): | |
def create_user(self, email=None, password=None, **extra_fields): | |
if not email: | |
raise ValueError('The given email must be set') | |
email = AccountManager.normalize_email(email) | |
user = self.model(email=email, | |
is_staff=False, is_active=True, is_superuser=False, | |
**extra_fields) | |
user.set_password(password) | |
user.save(using=self._db) | |
return user | |
def create_superuser(self, email, password, **extra_fields): | |
u = self.create_user(email, password, **extra_fields) | |
u.is_staff = True | |
u.is_active = True | |
u.is_superuser = True | |
u.save(using=self._db) | |
return u | |
def get_by_email(self, username): | |
return self.get(**{'email': username}) | |
class Account(AbstractBaseUser, PermissionsMixin): | |
GENDER_CHOICES = (('male', _('Male')), ('female', _('Female'))) | |
gender = models.CharField(_('gender'), max_length=20, choices=GENDER_CHOICES) | |
email = models.EmailField(_('email address'), max_length=110, unique=True) | |
birth_year = models.PositiveSmallIntegerField(_('birth year'), max_length=4) | |
zipcode = USZipModelField(_('zip'), max_length=20) | |
financial_institution = models.CharField(_("financial institution placeholder"), max_length=255) | |
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True) | |
is_staff = models.BooleanField('staff status', default=False, | |
help_text='Designates whether the user can log into this admin site.') | |
is_active = models.BooleanField('active', default=False, | |
help_text='Designates whether this user should be treated as ' | |
'active. Unselect this instead of deleting accounts.') | |
first_name = models.CharField(_('first name'), max_length=255, blank=True) | |
last_name = models.CharField(_('last name'), max_length=255, blank=True) | |
agree_to_terms_conditions = models.BooleanField(_('Agree to terms of use'), default=True) | |
token = models.CharField(max_length=40, blank=True, null=True) | |
USERNAME_FIELD = 'email' | |
objects = AccountManager() | |
def get_by_natural_key(self, username): | |
return self.get(**{self.model.USERNAME_FIELD: username}) | |
def get_full_name(self): | |
full_name = '%s %s' % (self.first_name, self.last_name) | |
return full_name.strip() | |
def get_short_name(self): | |
return self.first_name | |
def get_username(self): | |
return self.email | |
def __unicode__(self): | |
return self.email | |
class AccessHistory(models.Model): | |
user = models.ForeignKey(Account) | |
date = models.DateTimeField(_('access date'), auto_now_add=True) | |
ip_address = models.CharField(_('IP address'), blank=True, max_length=50) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
from django.test import TestCase | |
from django.test.client import Client | |
from django.conf import settings | |
from django.utils.translation import ugettext_lazy as _ | |
from django.forms.models import model_to_dict | |
from earnest.accounts.models import Account | |
from earnest.accounts.forms import RegisterUserFullForm, AdminRegisterUserFullForm | |
class AccountsTests(TestCase): | |
maxDiff = None | |
def test_index(self): | |
response = self.client.get('/') | |
self.assertEqual(response.status_code, 200) | |
REGISTER_FULL_EXPECTED_FORM_FIELDS = [ | |
'gender', | |
'email', | |
'zipcode', | |
'birth_year', | |
'financial_institution', | |
'agree_to_terms_conditions', | |
"password", | |
"password_confirm"] | |
REGISTER_FULL_ADMIN_EXPECTED_REQUIRED_FORM_FIELDS = [ | |
'gender', | |
'email', | |
'zipcode', | |
'birth_year', | |
'financial_institution', | |
"password", | |
"password_confirm"] | |
REGISTER_FULL_PUBLIC_EXPECTED_REQUIRED_FORM_FIELDS = REGISTER_FULL_EXPECTED_FORM_FIELDS | |
VALID_USER = { | |
'gender': 'male', | |
'email': '[email protected]', | |
'zipcode': '94209', | |
'birth_year': 1984, | |
'financial_institution': 'placeholder', | |
'password': '123A124aS', | |
'password_confirm': '123A124aS', | |
'agree_to_terms_conditions': 'on', | |
} | |
def test_register_forms_confirm_password_fail(self): | |
form = AdminRegisterUserFullForm(data={'password': 'passwordA2', 'password_confirm': "PasswordA3"}) | |
self.failIf(form.is_valid()) | |
self.assertIn(_("Passwords doesn't match."), form.errors['password_confirm']) | |
def test_register_user_in_admin_form_fields(self): | |
form = AdminRegisterUserFullForm() | |
self.assertEqual(set(form.fields.keys()), set(self.REGISTER_FULL_EXPECTED_FORM_FIELDS)) | |
def test_register_user_in_admin_confirm_password_success(self): | |
form = AdminRegisterUserFullForm(data={'password': 'passwordA2', 'password_confirm': "passwordA2"}) | |
self.failIf(form.is_valid()) | |
self.assertFalse(form.errors.get('password_confirm')) | |
def test_empty_public_registration_form(self): | |
#should throw all errors! | |
form = RegisterUserFullForm(data={}) | |
self.failIf(form.is_valid()) | |
self.assertEqual(set(form.errors.keys()), set(self.REGISTER_FULL_PUBLIC_EXPECTED_REQUIRED_FORM_FIELDS)) | |
def test_empty_admin_registration_form(self): | |
#should throw all errors! | |
form = AdminRegisterUserFullForm(data={}) | |
self.failIf(form.is_valid()) | |
self.assertEqual(set(form.errors.keys()), set(self.REGISTER_FULL_ADMIN_EXPECTED_REQUIRED_FORM_FIELDS)) | |
def help_test_register_forms(self): | |
#activate & login user | |
user = Account.objects.get_by_email(self.VALID_USER.get("email")) | |
response = self.client.login(username=self.VALID_USER.get("email"), password=self.VALID_USER.get("password")) | |
self.assertEqual(response, False) | |
user.is_active = True | |
user.save() | |
response = self.client.login(username=self.VALID_USER.get("email"), password=self.VALID_USER.get("password")) | |
self.assertEqual(response, True) | |
user_for_compare = self.VALID_USER.copy() | |
del user_for_compare['password'] | |
del user_for_compare['password_confirm'] | |
user_for_compare['agree_to_terms_conditions'] = True | |
user_for_compare['is_active'] = True | |
user_for_compare['is_superuser'] = False | |
user_for_compare['is_staff'] = False | |
user_for_compare['token'] = '' | |
user_for_compare['first_name'] = '' | |
user_for_compare['last_name'] = '' | |
user_from_db = model_to_dict(user) | |
del user_from_db['password'] | |
del user_from_db['id'] | |
del user_from_db['groups'] | |
del user_from_db['user_permissions'] | |
del user_from_db['last_login'] | |
self.assertEqual(set(user_from_db), set(user_for_compare)) | |
def test_valid_public_user_registration(self): | |
form = RegisterUserFullForm(data=self.VALID_USER) | |
self.assertTrue(form.is_valid()) | |
form.save() | |
self.help_test_register_forms() | |
def test_valid_admin_user_registration(self): | |
form = AdminRegisterUserFullForm(data=self.VALID_USER) | |
#form.is_valid() | |
#print (form.errors.keys()) | |
self.assertTrue(form.is_valid()) | |
form.save() | |
self.help_test_register_forms() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment