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

Support django 3 #108

Merged
merged 1 commit into from
Jan 12, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
9 changes: 6 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
language: python
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
- "3.9"

env:
- DJANGO_VERSION=1.11
- DJANGO_VERSION=2.0
- DJANGO_VERSION=2.2
- DJANGO_VERSION=3.0
- DJANGO_VERSION=3.1

# command to install dependencies
install:
Expand Down
13 changes: 6 additions & 7 deletions chamber/importers/__init__.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import csv
import io

from itertools import zip_longest

from django.conf import settings

import pyprind


try:
from itertools import zip_longest
except ImportError:
from itertools import izip_longest as zip_longest


def simple_count(file):
lines = 0
for _ in file:
Expand Down Expand Up @@ -127,13 +123,16 @@ def import_rows(self, reader, row_count=0):
self._post_batch_create(self.get_batch_size(), row_count)
del batch[:]
if any(row): # Skip blank lines
batch.append(self.model_class(**self.get_fields_dict(row)))
batch.append(self.create_instance(row))
created += self.create_batch(batch)
self._post_batch_create(len(batch), row_count)
self._post_import_rows(created)

return created

def create_instance(self, row):
return self.model_class(**self.get_fields_dict(row))

def get_delete_existing_objects(self):
return self.delete_existing_objects

Expand Down
104 changes: 64 additions & 40 deletions chamber/models/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
import collections

from itertools import chain

from distutils.version import StrictVersion

import django
Expand Down Expand Up @@ -60,11 +58,24 @@ def __bool__(self):
Unknown = UnknownSingleton()


@singleton
class DeferredSingleton:

def __repr__(self):
return 'deferred'

def __bool__(self):
return False


Deferred = DeferredSingleton()


def unknown_model_fields_to_dict(instance, fields=None, exclude=None):

return {
field.name: Unknown
for field in chain(instance._meta.concrete_fields, instance._meta.many_to_many) # pylint: disable=W0212
for field in instance._meta.concrete_fields # pylint: disable=W0212
Copy link

Choose a reason for hiding this comment

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

the chain import is no longer used

if not should_exclude_field(field, fields, exclude)
}

Expand All @@ -73,10 +84,9 @@ def model_to_dict(instance, fields=None, exclude=None):
"""
The same implementation as django model_to_dict but editable fields are allowed
"""

return {
field.name: field_to_dict(field, instance)
for field in chain(instance._meta.concrete_fields, instance._meta.many_to_many) # pylint: disable=W0212
for field in instance._meta.concrete_fields # pylint: disable=W0212
if not should_exclude_field(field, fields, exclude)
}

Expand All @@ -94,7 +104,7 @@ def __init__(self, initial_dict):

@property
def initial_values(self):
return self._initial_dict
return self._initial_dict.copy()

@property
def current_values(self):
Expand Down Expand Up @@ -134,9 +144,6 @@ def has_key(self, k):
def has_any_key(self, *keys):
return bool(set(self.keys()) & set(keys))

def update(self, *args, **kwargs):
raise AttributeError('Object is readonly')

def keys(self):
return self.diff.keys()

Expand Down Expand Up @@ -169,27 +176,41 @@ class DynamicChangedFields(ChangedFields):

def __init__(self, instance):
super().__init__(
self._get_unknown_dict(instance) if instance.is_adding else self._get_instance_dict(instance)
self._get_unknown_dict(instance)
)
self.instance = instance

def _get_unknown_dict(self, instance):
return unknown_model_fields_to_dict(
instance, fields=(field.name for field in instance._meta.fields)
)

def _get_instance_dict(self, instance):
return model_to_dict(
instance, fields=(field.name for field in instance._meta.fields)
)
return unknown_model_fields_to_dict(instance)

@property
def current_values(self):
return self._get_instance_dict(self.instance)
deferred_values = {
field_name: value for field_name, value in self._initial_dict.items()
if field_name in self.instance.get_deferred_fields()
}
current_values = model_to_dict(
self.instance,
exclude=set(deferred_values.keys())
)
current_values.update(deferred_values)
return current_values

def get_static_changes(self):
return StaticChangedFields(self.initial_values, self.current_values)

def from_db(self, fields=None):
if fields is None:
fields = {field_name for field_name, value in self._initial_dict.items() if value is not Deferred}

self._initial_dict.update(
model_to_dict(self.instance, fields=set(fields))
)

for field_name, value in self._initial_dict.items():
if value is Unknown:
self._initial_dict[field_name] = Deferred


class StaticChangedFields(ChangedFields):
"""
Expand All @@ -202,7 +223,7 @@ def __init__(self, initial_dict, current_dict):

@property
def current_values(self):
return self._current_dict
return self._current_dict.copy()


class ComparableModelMixin:
Expand Down Expand Up @@ -315,7 +336,7 @@ def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.is_adding = True
self.is_changing = False
self.changed_fields = DynamicChangedFields(self)
self._changed_fields = DynamicChangedFields(self)
self.post_save = Signal(self)

class Meta:
Expand All @@ -329,16 +350,24 @@ def from_db(cls, db, field_names, values):
new = super().from_db(db, field_names, values)
new.is_adding = False
new.is_changing = True
new.changed_fields = DynamicChangedFields(new)
updating_fields = [
f.name for f in cls._meta.concrete_fields
if len(values) == len(cls._meta.concrete_fields) or f.attname in field_names
]
new._changed_fields.from_db(fields=updating_fields)
return new

@property
def has_changed(self):
return bool(self.changed_fields)
return bool(self._changed_fields)

@property
def changed_fields(self):
return self._changed_fields.get_static_changes()

@property
def initial_values(self):
return self.changed_fields.initial_values
return self._changed_fields.initial_values

def full_clean(self, exclude=None, *args, **kwargs):
errors = {}
Expand Down Expand Up @@ -420,24 +449,24 @@ def _save(self, update_only_changed_fields=False, is_cleaned_pre_save=None, is_c
kwargs.update(self._get_save_extra_kwargs())

self._call_pre_save(
changed=self.is_changing, changed_fields=self.changed_fields.get_static_changes(), *args, **kwargs
changed=self.is_changing, changed_fields=self.changed_fields, *args, **kwargs
)
if is_cleaned_pre_save:
self._clean_pre_save(*args, **kwargs)
dispatcher_pre_save.send(
sender=origin, instance=self, changed=self.is_changing,
changed_fields=self.changed_fields.get_static_changes(),
changed_fields=self.changed_fields,
*args, **kwargs
)

if not update_fields and update_only_changed_fields:
update_fields = list(self.changed_fields.keys()) + ['changed_at']
update_fields = list(self._changed_fields.keys()) + ['changed_at']
# remove primary key from updating fields
if self._meta.pk.name in update_fields:
update_fields.remove(self._meta.pk.name)

# Changed fields must be cached before save, for post_save and signal purposes
post_save_changed_fields = self.changed_fields.get_static_changes()
post_save_changed_fields = self.changed_fields
post_save_is_changing = self.is_changing

self.save_simple(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
Expand Down Expand Up @@ -468,7 +497,7 @@ def save_simple(self, *args, **kwargs):
super().save(*args, **kwargs)
self.is_adding = False
self.is_changing = True
self.changed_fields = DynamicChangedFields(self)
self._changed_fields.from_db()

def save(self, update_only_changed_fields=False, *args, **kwargs):
if self._smart_meta.is_save_atomic:
Expand Down Expand Up @@ -510,22 +539,17 @@ def delete(self, *args, **kwargs):
else:
self._delete(*args, **kwargs)

def refresh_from_db(self, *args, **kwargs):
super().refresh_from_db(*args, **kwargs)
def refresh_from_db(self, using=None, fields=None):
super().refresh_from_db(using=using, fields=fields)
for key, value in self.__class__.__dict__.items():
if isinstance(value, cached_property):
self.__dict__.pop(key, None)
self.is_adding = False
self.is_changing = True
self.changed_fields = DynamicChangedFields(self)

if StrictVersion(get_main_version()) < StrictVersion('2.0'):
for field in [f for f in self._meta.get_fields() if f.is_relation]:
# For Generic relation related model is None
# https://docs.djangoproject.com/en/2.1/ref/models/meta/#migrating-from-the-old-api
cache_key = field.get_cache_name() if field.related_model else field.cache_attr
if cache_key in self.__dict__:
del self.__dict__[cache_key]

self._changed_fields.from_db(fields={
f.name for f in self._meta.concrete_fields if not fields or f.attname in fields or f.name in fields
})

return self

Expand Down
9 changes: 2 additions & 7 deletions chamber/multidomains/domain.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from urllib.parse import urlparse

from django.apps import apps
from django.core.exceptions import ImproperlyConfigured


Expand Down Expand Up @@ -38,13 +39,7 @@ def url(self):

@property
def user_class(self):
try:
from django.apps import apps
get_model = apps.get_model
except ImportError:
from django.db.models.loading import get_model

return get_model(*self.user_model.split('.', 1))
return apps.get_model(*self.user_model.split('.', 1))


def get_domain(site_id):
Expand Down
6 changes: 1 addition & 5 deletions chamber/multidomains/urlresolvers.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
from urllib.parse import urlencode

from django.conf import settings

try:
from django.core.urlresolvers import reverse as django_reverse
except ImportError:
from django.urls import reverse as django_reverse
from django.urls import reverse as django_reverse



Expand Down
21 changes: 14 additions & 7 deletions chamber/utils/migrations/fixtures.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

from io import StringIO


from django.db import DEFAULT_DB_ALIAS
from django.core.management import call_command
from django.core.serializers import base, python
from django.core.serializers import python, base
from django.core.management.commands import loaddata


class MigrationLoadFixture:
Expand All @@ -26,9 +29,13 @@ def _get_model(model_identifier):
raise base.DeserializationError("Invalid model identifier: '%s'" % model_identifier)

get_model_tmp = python._get_model # pylint: disable=W0212
python._get_model = _get_model
file = os.path.join(self.fixture_dir, self.fixture_filename)
if not os.path.isfile(file):
raise IOError('File "%s" does not exists' % file)
call_command('loaddata', file, stdout=StringIO())
python._get_model = get_model_tmp # pylint: disable=W0212
try:
python._get_model = _get_model
file = os.path.join(self.fixture_dir, self.fixture_filename)
if not os.path.isfile(file):
raise IOError('File "%s" does not exists' % file)
loaddata.Command().handle(
file, ignore=True, database=DEFAULT_DB_ALIAS, app_label=None, verbosity=0, exclude=[], format='json'
)
finally:
python._get_model = get_model_tmp # pylint: disable=W0212
2 changes: 1 addition & 1 deletion example/dj/apps/test_chamber/tests/importers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from six import StringIO
from io import StringIO

from django.core.management import call_command
from django.test import TestCase
Expand Down