diff options
| author | Magnus Hagander | 2020-04-12 18:06:16 +0000 |
|---|---|---|
| committer | Magnus Hagander | 2020-04-12 18:06:16 +0000 |
| commit | aa5ed77d3ce8f4fc8450ff4d8b2a1a5ede2a4366 (patch) | |
| tree | 67b430c1c35d805459f7b966cc8cabb9aac04d3b /postgresqleu/util/db.py | |
| parent | 0602ff9e54b1b77c5be7a180e781a8d84d02e2ed (diff) | |
Enable full timezone management
Switch the system to properly use django and postgres timezone support,
by allowing each conference to render all date related information in a
conference specific timezone (using the one that has already been
specified on the conference, per a previous commit).
All non-conference parts of the system keep using the default timezone
as specified in settings.TIME_ZONE.
This includes a migration that updates the existing sessions, session
slots and volunteer slots based on what timezone has been configured
for the conference (since previously everything was stored in the
wrong timezone if the conference was in anything but the default
one).
In order to make this work for non-django-orm queries, a context
manager that swaps the timezone to the conference and back out is
introduced, and related to that a way to get a cursor that turns off
django's protection against doing exactly this.
This finally removes the very ugly "timediff" column on the conference
which was a quick hack back in the days to support ical feeds using utc.
In passing, this also:
* Fixes ical feeds to include all required fields (uid and dtstamp
were missing on schedule entries)
* Fixes xml feed to use conference local time (fixes #8)
* Clarify what "valid until" and "active until" means in the help text
on discount codes and registration tpes.
* Don't duplicate dates in schedule xml feeds (seems others don't, and
there is no clear spec anywhere that I can find)
Diffstat (limited to 'postgresqleu/util/db.py')
| -rw-r--r-- | postgresqleu/util/db.py | 52 |
1 files changed, 44 insertions, 8 deletions
diff --git a/postgresqleu/util/db.py b/postgresqleu/util/db.py index c9d8f5c3..e0a07381 100644 --- a/postgresqleu/util/db.py +++ b/postgresqleu/util/db.py @@ -1,33 +1,43 @@ from django.db import connection +from django.conf import settings import collections +from psycopg2.tz import LocalTimezone -def exec_no_result(query, params=None): + +def get_native_cursor(): + # Remove djangos dumb prevention of us to change the timezone curs = connection.cursor() + curs.cursor.tzinfo_factory = LocalTimezone + return curs + + +def exec_no_result(query, params=None): + curs = get_native_cursor() curs.execute(query, params) def exec_to_list(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) return curs.fetchall() def exec_to_single_list(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) return [r[0] for r in curs.fetchall()] def exec_to_dict(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) columns = [col[0] for col in curs.description] return [dict(list(zip(columns, row)))for row in curs.fetchall()] def exec_to_scalar(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) r = curs.fetchone() if r: @@ -44,20 +54,20 @@ def conditional_exec_to_scalar(condition, query, params=None): def exec_to_keyed_dict(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) columns = [col[0] for col in curs.description] return {r[columns[0]]: r for r in (dict(list(zip(columns, row)))for row in curs.fetchall())} def exec_to_keyed_scalar(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) return dict(curs.fetchall()) def exec_to_grouped_dict(query, params=None): - curs = connection.cursor() + curs = get_native_cursor() curs.execute(query, params) columns = [col[0] for col in curs.description[1:]] full = collections.OrderedDict() @@ -73,3 +83,29 @@ def exec_to_grouped_dict(query, params=None): if last: full[last] = curr return full + + +class ensure_conference_timezone(): + """ + This context handler will set the timezone *in PostgreSQL* to the one from the + specified conference, and reset it back to UTC when it's done. During this time, + calls from django's own ORM will fail, it's intended only to wrap native SQL + querying that needs to use the appropriate timezone. + If None is specified as the conference, set the timezone to the global one for + the installation (otherwise the default is UTC). + """ + def __init__(self, conference): + if conference is None: + self.tzname = settings.TIME_ZONE + else: + self.tzname = conference.tzname + + def __enter__(self): + c = connection.cursor() + c.execute("SET TIMEZONE=%(tz)s", { + 'tz': self.tzname, + }) + return c + + def __exit__(self, *args): + connection.cursor().execute("SET TIMEZONE='UTC'") |
