diff --git a/runtests.py b/runtests.py index d136086..60d09aa 100755 --- a/runtests.py +++ b/runtests.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import absolute_import import django from django.conf import settings from django.core.management import call_command diff --git a/setup.py b/setup.py index 108c78a..34feddd 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +from __future__ import absolute_import from setuptools import setup, find_packages setup( diff --git a/synchro/apps.py b/synchro/apps.py index 7073200..2cf4105 100644 --- a/synchro/apps.py +++ b/synchro/apps.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from django.apps import AppConfig @@ -6,5 +7,5 @@ class SynchroConfig(AppConfig): verbose_name = 'Synchro' def ready(self): - from signals import synchro_connect + from .signals import synchro_connect synchro_connect() diff --git a/synchro/core.py b/synchro/core.py index 8dcf883..8722b41 100644 --- a/synchro/core.py +++ b/synchro/core.py @@ -1,3 +1,4 @@ -from utility import NaturalManager, reset_synchro -from management.commands.synchronize import call_synchronize -from signals import DisableSynchroLog, disable_synchro_log +from __future__ import absolute_import +from .utility import NaturalManager, reset_synchro +from .management.commands.synchronize import call_synchronize +from .signals import DisableSynchroLog, disable_synchro_log diff --git a/synchro/handlers.py b/synchro/handlers.py index c524407..c790ad3 100644 --- a/synchro/handlers.py +++ b/synchro/handlers.py @@ -1,6 +1,7 @@ -import settings +from __future__ import absolute_import +from . import settings settings.prepare() -from models import ChangeLog, DeleteKey, ADDITION, CHANGE, DELETION, M2M_CHANGE +from .models import ChangeLog, DeleteKey, ADDITION, CHANGE, DELETION, M2M_CHANGE def delete_redundant_change(cl): diff --git a/synchro/management/commands/synchronize.py b/synchro/management/commands/synchronize.py index 6628b2d..f34c71c 100644 --- a/synchro/management/commands/synchronize.py +++ b/synchro/management/commands/synchronize.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from datetime import datetime from django import VERSION @@ -10,6 +11,7 @@ from synchro.models import Reference, ChangeLog, DeleteKey, options as app_options from synchro.models import ADDITION, CHANGE, DELETION, M2M_CHANGE from synchro.settings import REMOTE, LOCAL +import six if not hasattr(transaction, 'atomic'): @@ -120,7 +122,7 @@ def save_m2m(ct, obj, remote): _m2m = {} # handle m2m fields - for f, (to, through, me, he_id) in M2M_CACHE[model_name].iteritems(): + for f, (to, through, me, he_id) in six.iteritems(M2M_CACHE[model_name]): fk_ct = ContentType.objects.get_for_model(to) out = [] if through._meta.auto_created: @@ -135,7 +137,7 @@ def save_m2m(ct, obj, remote): out.append(inter) _m2m[f] = not through._meta.auto_created, out - for f, (intermediary, out) in _m2m.iteritems(): + for f, (intermediary, out) in six.iteritems(_m2m): if not intermediary: setattr(remote, f, out) else: @@ -262,7 +264,8 @@ def synchronize(self, *args, **options): since = app_options.last_check last_time = datetime.now() - logs = ChangeLog.objects.filter(date__gt=since).select_related().order_by('date', 'pk') + filters = {"date__gt": since} if since else {} + logs = ChangeLog.objects.filter(**filters).select_related().order_by('date', 'pk') # Don't synchronize if object should be added/changed and later deleted; to_del = {} diff --git a/synchro/migrations/0001_initial.py b/synchro/migrations/0001_initial.py new file mode 100644 index 0000000..973de1d --- /dev/null +++ b/synchro/migrations/0001_initial.py @@ -0,0 +1,50 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.15 on 2019-09-30 08:41 +from __future__ import unicode_literals + +from __future__ import absolute_import +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + ] + + operations = [ + migrations.CreateModel( + name='ChangeLog', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('object_id', models.CharField(max_length=256)), + ('date', models.DateTimeField(auto_now=True)), + ('action', models.PositiveSmallIntegerField(choices=[(1, 'Add'), (2, 'Change'), (3, 'Delete'), (4, 'M2m Change')])), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + migrations.CreateModel( + name='DeleteKey', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('key', models.CharField(max_length=256)), + ('changelog', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='synchro.ChangeLog')), + ], + ), + migrations.CreateModel( + name='Reference', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('local_object_id', models.CharField(max_length=256)), + ('remote_object_id', models.CharField(max_length=256)), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), + ], + ), + migrations.AlterUniqueTogether( + name='reference', + unique_together=set([('content_type', 'local_object_id')]), + ), + ] diff --git a/synchro/migrations/__init__.py b/synchro/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/synchro/models.py b/synchro/models.py index 6a31b34..a8332f7 100644 --- a/synchro/models.py +++ b/synchro/models.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import django from django.contrib.admin.models import ADDITION, CHANGE, DELETION from django.contrib.contenttypes.models import ContentType @@ -5,6 +6,7 @@ from django.db import models from django.utils.timezone import now import dbsettings +import six M2M_CHANGE = 4 @@ -24,8 +26,8 @@ class SynchroSettings(dbsettings.Group): class Reference(models.Model): content_type = models.ForeignKey(ContentType) - local_object_id = models.CharField(max_length=20) - remote_object_id = models.CharField(max_length=20) + local_object_id = models.CharField(max_length=256) + remote_object_id = models.CharField(max_length=256) class Meta: unique_together = ('content_type', 'local_object_id') @@ -33,15 +35,15 @@ class Meta: class ChangeLog(models.Model): content_type = models.ForeignKey(ContentType) - object_id = models.CharField(max_length=20) + object_id = models.CharField(max_length=256) object = GenericForeignKey() date = models.DateTimeField(auto_now=True) action = models.PositiveSmallIntegerField(choices=ACTIONS) def __unicode__(self): - return u'ChangeLog for %s (%s)' % (unicode(self.object), self.get_action_display()) + return u'ChangeLog for %s (%s)' % (six.text_type(self.object), self.get_action_display()) class DeleteKey(models.Model): changelog = models.OneToOneField(ChangeLog) - key = models.CharField(max_length=200) + key = models.CharField(max_length=256) diff --git a/synchro/settings.py b/synchro/settings.py index 59a896c..16a43be 100644 --- a/synchro/settings.py +++ b/synchro/settings.py @@ -1,6 +1,8 @@ +from __future__ import absolute_import from django.apps import apps from django.conf import settings from django.core.exceptions import ImproperlyConfigured +from six.moves import map def get_all_models(app): @@ -21,7 +23,7 @@ def parse(model): raise ImproperlyConfigured( 'SYNCHRO_MODELS: Model %s not found in %s app.' % (model, app)) return m - return map(parse, l) + return list(map(parse, l)) def parse_models(l): diff --git a/synchro/signals.py b/synchro/signals.py index de1618c..7897bb4 100644 --- a/synchro/signals.py +++ b/synchro/signals.py @@ -1,10 +1,11 @@ +from __future__ import absolute_import from functools import wraps from django.db.models.signals import post_save, post_delete, m2m_changed def synchro_connect(): - from handlers import save_changelog_add_chg, save_changelog_del, save_changelog_m2m + from .handlers import save_changelog_add_chg, save_changelog_del, save_changelog_m2m post_save.connect(save_changelog_add_chg, dispatch_uid='synchro_add_chg') post_delete.connect(save_changelog_del, dispatch_uid='synchro_del') m2m_changed.connect(save_changelog_m2m, dispatch_uid='synchro_m2m') diff --git a/synchro/test_urls.py b/synchro/test_urls.py index eb43781..8eeac7d 100644 --- a/synchro/test_urls.py +++ b/synchro/test_urls.py @@ -1,4 +1,5 @@ # flake8: noqa +from __future__ import absolute_import from django.contrib import admin from django.conf.urls import url, include diff --git a/synchro/tests.py b/synchro/tests.py index 295cc68..9bfb542 100644 --- a/synchro/tests.py +++ b/synchro/tests.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import import datetime from django import VERSION @@ -16,10 +17,10 @@ except ImportError: from django.utils.unittest.case import skipUnless -from models import ChangeLog -import settings as synchro_settings -from signals import DisableSynchroLog, disable_synchro_log -from utility import NaturalManager, reset_synchro, NaturalKeyModel +from .models import ChangeLog +from . import settings as synchro_settings +from .signals import DisableSynchroLog, disable_synchro_log +from .utility import NaturalManager, reset_synchro, NaturalKeyModel from django.contrib.auth import get_user_model User = get_user_model() @@ -621,7 +622,7 @@ def test_foreign_keys(self): b = PkModelWithSkip.objects.db_manager(REMOTE).get(name='James') self.assertEqual(2, b.links.count()) # Check if all submodels belong to remote db - self.assertTrue(all(map(lambda x: x._state.db == REMOTE, b.links.all()))) + self.assertTrue(all([x._state.db == REMOTE for x in b.links.all()])) def test_disabling(self): """Test if logging can be disabled.""" diff --git a/synchro/urls.py b/synchro/urls.py index 975c958..c77753f 100644 --- a/synchro/urls.py +++ b/synchro/urls.py @@ -1,7 +1,8 @@ # flake8: noqa +from __future__ import absolute_import from django.conf.urls import url -from views import synchro +from .views import synchro urlpatterns = ( diff --git a/synchro/utility.py b/synchro/utility.py index ea5ee0d..4460dc6 100644 --- a/synchro/utility.py +++ b/synchro/utility.py @@ -1,8 +1,11 @@ +from __future__ import absolute_import from datetime import datetime from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.db.models import Manager, Model from django.db.models.base import ModelBase +import six +from six.moves import zip class NaturalManager(Manager): @@ -14,7 +17,7 @@ class NaturalManager(Manager): allow_many = False def get_by_natural_key(self, *args): - lookups = dict(zip(self.fields, args)) + lookups = dict(list(zip(self.fields, args))) try: return self.get(**lookups) except MultipleObjectsReturned: @@ -27,7 +30,7 @@ def __new__(cls, *fields, **options): Creates actual manager, which can be further subclassed and instantiated without arguments. """ if ((not fields and hasattr(cls, 'fields') and hasattr(cls, 'allow_many')) or - fields and not isinstance(fields[0], basestring)): + fields and not isinstance(fields[0], six.string_types)): # Class was already prepared. return super(NaturalManager, cls).__new__(cls) @@ -63,8 +66,7 @@ def __new__(cls, name, bases, attrs): return super(_NaturalKeyModelBase, cls).__new__(cls, name, bases, attrs) -class NaturalKeyModel(Model): - __metaclass__ = _NaturalKeyModelBase +class NaturalKeyModel(six.with_metaclass(_NaturalKeyModelBase, Model)): _natural_key = () def natural_key(self): @@ -75,7 +77,7 @@ class Meta: def reset_synchro(): - from models import ChangeLog, Reference, options + from .models import ChangeLog, Reference, options options.last_check = datetime.now() ChangeLog.objects.all().delete() Reference.objects.all().delete() diff --git a/synchro/views.py b/synchro/views.py index 1ac6d7e..d748437 100644 --- a/synchro/views.py +++ b/synchro/views.py @@ -1,3 +1,4 @@ +from __future__ import absolute_import from django.contrib.admin.views.decorators import staff_member_required from django.contrib import messages from django.template.response import TemplateResponse