Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TimeZoneMixin #143

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Upcoming

* Add `TimeZoneMixin` for custom `User` models.

## v14.0.0

* Clarify error message when your old and new passwords match, you will need to update translations.
Expand Down
10 changes: 10 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/usr/bin/env python
import os
import sys

if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_management.tests.settings")

from django.core.management import execute_from_command_line

execute_from_command_line(sys.argv)
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ coverage==3.7.1
dj-database-url==0.3.0
dj-inmemorystorage==1.3.0
factory_boy==2.5.2
flake8==2.3.0
flake8==2.5.0
flake8-import-order==0.5.1
incuna-test-utils==6.0.0
mkdocs==0.11.1
Expand Down
12 changes: 12 additions & 0 deletions user_management/models/mixins.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import pytz
from django.contrib.auth.models import BaseUserManager
from django.contrib.auth.tokens import default_token_generator
from django.contrib.sites.models import Site
Expand All @@ -9,6 +10,7 @@
from django.utils.translation import ugettext_lazy as _

from user_management.utils import notifications
from .utils import timezone_choices


class UserManager(BaseUserManager):
Expand Down Expand Up @@ -55,6 +57,16 @@ class Meta:
abstract = True


class TimeZoneMixin(models.Model):
timezone = models.CharField(
max_length=255,
choices=timezone_choices(pytz.common_timezones),
)

class Meta:
abstract = True


class EmailUserMixin(models.Model):
email = models.EmailField(
verbose_name=_('Email address'),
Expand Down
19 changes: 14 additions & 5 deletions user_management/models/tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,23 @@
EmailVerifyUserMixin,
IsStaffUserMixin,
NameUserMethodsMixin,
TimeZoneMixin,
VerifyEmailMixin,
)


class User(AvatarMixin, VerifyEmailMixin, PermissionsMixin, AbstractBaseUser):
pass
class User(
AvatarMixin, TimeZoneMixin, VerifyEmailMixin, PermissionsMixin,
AbstractBaseUser):
"""A User model using all the custom mixins."""


class BasicUser(BasicUserFieldsMixin, AbstractBaseUser):
pass
"""A User model using just the BasicUserFieldsMixin."""


class VerifyEmailUser(VerifyEmailMixin, AbstractBaseUser):
pass
"""A User model using just the VerifyEmailMixin."""


class CustomVerifyEmailUser(VerifyEmailMixin, AbstractBaseUser):
Expand All @@ -35,6 +38,12 @@ class CustomVerifyEmailUser(VerifyEmailMixin, AbstractBaseUser):
class CustomBasicUserFieldsMixin(
NameUserMethodsMixin, EmailUserMixin, DateJoinedUserMixin,
IsStaffUserMixin):
"""
A replacement for BasicUserFieldsMixin with a custom name field.

Uses NameUserMethodsMixin instead of NameUserMixin.
"""

name = models.TextField()

USERNAME_FIELD = 'email'
Expand All @@ -46,4 +55,4 @@ class Meta:
class CustomNameUser(
AvatarMixin, EmailVerifyUserMixin, CustomBasicUserFieldsMixin,
AbstractBaseUser):
pass
"""A User model using the CustomBasicUserFieldsMixin."""
1 change: 1 addition & 0 deletions user_management/models/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ def test_fields(self):
'last_login',
'password',
'avatar',
'timezone',

# Incoming
'groups', # Django permission groups
Expand Down
53 changes: 53 additions & 0 deletions user_management/models/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from unittest import TestCase

from ..utils import timezone_choices


class TestTimeZoneChoices(TestCase):
timezones = [
'Arctic/Longyearbyen',
'Asia/Qyzylorda',
'America/Argentina/Ushuaia',
'America/Iqaluit',
'GMT',
'Indian/Christmas',
'UTC',
]

def setUp(self):
self.choices = timezone_choices(self.timezones)

def test_exclude_GMT_UTC(self):
self.assertNotIn('GMT', self.choices)
self.assertNotIn('UTC', self.choices)

def test_choice_groups(self):
expected_choices = (
(
'Arctic',
(
('Arctic/Longyearbyen', 'Longyearbyen'),
),
),
(
'Asia',
(
('Asia/Qyzylorda', 'Qyzylorda'),
),
),
(
'America',
(
('America/Argentina/Ushuaia', 'Argentina/Ushuaia'),
('America/Iqaluit', 'Iqaluit'),
),
),
(
'Indian',
(
('Indian/Christmas', 'Christmas'),
),
),
)

self.assertEqual(self.choices, expected_choices)
20 changes: 20 additions & 0 deletions user_management/models/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from collections import defaultdict, OrderedDict


class DefaultOrderedDict(OrderedDict, defaultdict):
def __init__(self, default_factory=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.default_factory = default_factory


def timezone_choices(timezones, exclude=('GMT', 'UTC')):
choices = DefaultOrderedDict(list)

for timezone in timezones:
if timezone in exclude:
continue

continent, _sep, town = timezone.partition('/')
choices[continent].append((timezone, town))

return tuple((c, tuple(t)) for c, t in choices.items())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cool, I wasn't aware django could deal with grouped choices

35 changes: 35 additions & 0 deletions user_management/tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
SECRET_KEY = 'not-for-production'
DEBUG = True

ALLOWED_HOSTS = []

INSTALLED_APPS = (
# Put contenttypes before auth to work around test issue.
# See: https://code.djangoproject.com/ticket/10827#comment:12
'django.contrib.sites',
'django.contrib.contenttypes',
'django.contrib.auth',
'django.contrib.sessions',
'django.contrib.admin',

'rest_framework.authtoken',

# Added for templates
'user_management.api',
'user_management.models.tests',
)

AUTH_USER_MODEL = 'tests.User'

MIDDLEWARE_CLASSES = ()

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True

MIGRATION_MODULES = {
'api': 'user_management.tests.testmigrations.api',
'tests': 'user_management.tests.testmigrations.tests',
}
19 changes: 19 additions & 0 deletions user_management/tests/testmigrations/tests/0002_user_timezone.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('tests', '0001_initial'),
]

operations = [
migrations.AddField(
model_name='user',
name='timezone',
field=models.CharField(default='UTC', max_length=255),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you need to update the test migration?

),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import pytz
from django.db import migrations, models
from user_management.models.utils import timezone_choices


class Migration(migrations.Migration):

dependencies = [
('tests', '0002_user_timezone'),
]

operations = [
migrations.AlterField(
model_name='user',
name='timezone',
field=models.CharField(choices=timezone_choices(pytz.common_timezones), max_length=255),
),
]