authentic/src/authentic2/compat.py

124 lines
4.2 KiB
Python

from datetime import datetime
import inspect
import django
from django.conf import settings
from django.db import connection
from django.db.utils import OperationalError
from django.core.exceptions import ImproperlyConfigured
from django.contrib.auth.tokens import PasswordResetTokenGenerator
try:
from django.contrib.auth import get_user_model
except ImportError:
from django.contrib.auth.models import User
get_user_model = lambda: User
try:
from django.db.transaction import atomic
commit_on_success = atomic
except ImportError:
from django.db.transaction import commit_on_success
user_model_label = getattr(settings, 'AUTH_USER_MODEL', 'auth.User')
default_token_generator = PasswordResetTokenGenerator()
def has_postgresql_support():
try:
return connection.vendor == 'postgresql' and connection.pg_version > 90400
except (ImproperlyConfigured, OperationalError):
# database is not initialized, be conservative
return False
def use_django_native_field():
return has_postgresql_support() and django.VERSION >= (1, 11)
class JSONField(object):
__dj11_field = None
__jsonfield_field = None
__name = None
def __init__(self, *args, **kwargs):
self.__args = args
self.__kwargs = kwargs
if django.VERSION >= (1, 11):
try:
from django.contrib.postgres.fields import JSONField
self.__dj11_field = JSONField(*args, **kwargs)
except ImportError:
pass
try:
from jsonfield.fields import JSONField
self.__jsonfield_field = JSONField(*args, **kwargs)
except ImportError:
pass
def __real_field__(self):
if use_django_native_field():
assert self.__dj11_field
return self.__dj11_field
assert self.__jsonfield_field
return self.__jsonfield_field
def __getattr__(self, key):
return getattr(self.__real_field__(), key)
def __setattr__(self, key, value):
if key.startswith('_JSONField__'):
super(JSONField, self).__setattr__(key, value)
else:
setattr(self.__real__field(), key, value)
# we need to implement contribute_to_class so that the direct
# implementation from the two sub-fields is not used directly
def contribute_to_class(self, cls, name, private_only=False, virtual_only=False, **kwargs):
assert not virtual_only and not private_only, 'virtual_only / private_only are not supported'
assert not kwargs, 'new arguments to contribute_to_class not supported'
self.__name = name
if self.__dj11_field:
self.__dj11_field.set_attributes_from_name(name)
self.__dj11_field.model = cls
if self.__jsonfield_field:
self.__jsonfield_field.set_attributes_from_name(name)
self.__jsonfield_field.model = cls
cls._meta.add_field(self)
# the next two methods are useful for compatibilit with the migration engine
# inspect is used because migration autodetector cannot recognize this class
# as a subclass of models.Field.
def deconstruct(self):
d = (self.__name, 'authentic2.compat.JSONField', self.__args, self.__kwargs)
previous_frame = inspect.currentframe().f_back
if inspect.getframeinfo(previous_frame)[2] in ('serialize', 'deep_deconstruct'):
d = d[1:]
return d
def clone(self):
from copy import copy
new = copy(self)
if self.__dj11_field:
new.__dj11_field = new.__dj11_field.clone()
if self.__jsonfield_field:
new.__jsonfield_field = new.__jsonfield_field.clone()
return new
try:
import jsonfield.fields
except ImportError:
pass
else:
# prevent django-jsonfield from modifying postgresql connection when we are
# not using it
if hasattr(jsonfield.fields, 'connection_created'):
def configure_database_connection(connection, **kwargs):
if django.VERSION < (1, 11):
jsonfield.fields.configure_database_connection(connection, **kwargs)
jsonfield.fields.connection_created.disconnect(jsonfield.fields.configure_database_connection)
jsonfield.fields.connection_created.connect(configure_database_connection)