Skip to content
This repository was archived by the owner on Apr 18, 2025. It is now read-only.

Commit cf1dc13

Browse files
committed
Add per-user timezone setting
The `0002` migration is to make it so it doesn't show as needing a migration if Django's list of timezones ever changes. https://dev.to/nessita/django-migrations-by-choice-32n7
1 parent b74f85f commit cf1dc13

File tree

10 files changed

+63
-1
lines changed

10 files changed

+63
-1
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ repos:
1414
rev: 23.3.0
1515
hooks:
1616
- id: black
17+
exclude: ^core/migrations/
1718
- repo: https://github.com/pycqa/flake8
1819
rev: 6.0.0
1920
hooks:

config/settings.py-tpl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ from django.core.management.utils import get_random_secret_key
1414
from pathlib import Path
1515
from environs import Env
1616
from warnings import filterwarnings
17+
from pytz import common_timezones
1718

1819
env = Env()
1920
# Read .env into os.environ
@@ -72,6 +73,7 @@ MIDDLEWARE = [
7273
"django.contrib.auth.middleware.AuthenticationMiddleware",
7374
"django.contrib.messages.middleware.MessageMiddleware",
7475
"django.middleware.clickjacking.XFrameOptionsMiddleware",
76+
"core.middleware.TimezoneMiddleware",
7577
"debug_toolbar.middleware.DebugToolbarMiddleware",
7678
"django_htmx.middleware.HtmxMiddleware",
7779
"django_browser_reload.middleware.BrowserReloadMiddleware",
@@ -134,6 +136,8 @@ LANGUAGE_CODE = "en-us"
134136

135137
TIME_ZONE = "UTC"
136138

139+
TIME_ZONES = [(tz, tz) for tz in common_timezones]
140+
137141
USE_I18N = True
138142

139143
USE_TZ = True

core/admin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class UserAdmin(DjangoUserAdmin):
1111

1212
fieldsets = (
1313
(None, {"fields": ("email", "password")}),
14-
(_("Personal info"), {"fields": ("first_name", "last_name")}),
14+
(_("Personal info"), {"fields": ("first_name", "last_name", "timezone")}),
1515
(
1616
_("Permissions"),
1717
{

core/middleware.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import pytz
2+
from django.utils import timezone
3+
from django.conf import settings
4+
5+
6+
class TimezoneMiddleware:
7+
"""
8+
Automatically set the timezone to what the user has chosen.
9+
"""
10+
11+
def __init__(self, get_response):
12+
self.get_response = get_response
13+
14+
def __call__(self, request):
15+
if request.user.is_authenticated:
16+
tzname = request.user.timezone
17+
if tzname:
18+
timezone.activate(pytz.timezone(tzname))
19+
else:
20+
timezone.activate(pytz.timezone(settings.TIME_ZONE))
21+
else:
22+
timezone.deactivate()
23+
24+
return self.get_response(request)

core/models.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from django.contrib.auth.models import AbstractUser, BaseUserManager
22
from django.db import models
33
from django.utils.translation import gettext_lazy as _
4+
from django.conf import settings
45

56

67
class UserManager(BaseUserManager):
@@ -42,6 +43,12 @@ class User(AbstractUser):
4243

4344
username = None
4445
email = models.EmailField(_("email address"), unique=True)
46+
timezone = models.CharField(
47+
max_length=40,
48+
blank=True,
49+
choices=settings.TIME_ZONES,
50+
default=settings.TIME_ZONE,
51+
)
4552
# Add more fields here.
4653

4754
objects = UserManager()

dev/0002_adjust_timezone_migration.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
from django.db import migrations, models
2+
from django.conf import settings
3+
4+
5+
class Migration(migrations.Migration):
6+
dependencies = [
7+
("core", "0001_initial"),
8+
]
9+
10+
operations = [
11+
migrations.AlterField(
12+
model_name="user",
13+
name="timezone",
14+
field=models.CharField(
15+
blank=True,
16+
choices=settings.TIME_ZONES,
17+
default=settings.TIME_ZONE,
18+
max_length=40,
19+
),
20+
),
21+
]

dev/setup.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ gum style --border normal --margin "1" --padding "0 2" --border-foreground 212 \
168168
"Warming up $(gum style --foreground 212 'database') and $(gum style --foreground 212 'static files')"
169169
gum spin --title "Collecting static files" -- python manage.py collectstatic
170170
gum spin --title "Warming up the database" -- python manage.py makemigrations
171+
gum spin --title "Warming up the database" -- mv dev/0002_adjust_timezone_migration.py core/migrations/0002_adjust_timezone_migration.py
171172
gum spin --title "Warming up the database" -- python manage.py migrate
172173
gum spin --title "Creating initial superuser account" -- python manage.py createsuperuser --noinput --email=$EMAIL
173174

dev/test-build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ case $yn in
1515
.venv/bin/pip-compile --resolver=backtracking requirements/requirements.in
1616
.venv/bin/python -m pip install -r requirements/requirements.txt
1717
.venv/bin/python manage.py makemigrations
18+
mv dev/0002_adjust_timezone_migration.py core/migrations/0002_adjust_timezone_migration.py
1819
.venv/bin/python manage.py migrate --noinput
1920
.venv/bin/python manage.py collectstatic --noinput
2021
source $HOME/.nvm/nvm.sh

requirements/requirements.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,5 @@ pip-lock
1717
pip-tools
1818
pre-commit
1919
pytest-django
20+
pytz
2021
whitenoise

requirements/requirements.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ pytest-django==4.8.0
157157
# via -r requirements/requirements.in
158158
python-dotenv==1.0.1
159159
# via environs
160+
pytz==2024.1
161+
# via -r requirements/requirements.in
160162
pyyaml==6.0.1
161163
# via
162164
# djlint

0 commit comments

Comments
 (0)