Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions runtests.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/usr/bin/env python
from __future__ import absolute_import
from setuptools import setup, find_packages

setup(
Expand Down
3 changes: 2 additions & 1 deletion synchro/apps.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import absolute_import
from django.apps import AppConfig


Expand All @@ -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()
7 changes: 4 additions & 3 deletions synchro/core.py
Original file line number Diff line number Diff line change
@@ -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
5 changes: 3 additions & 2 deletions synchro/handlers.py
Original file line number Diff line number Diff line change
@@ -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):
Expand Down
9 changes: 6 additions & 3 deletions synchro/management/commands/synchronize.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import absolute_import
from datetime import datetime

from django import VERSION
Expand All @@ -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'):
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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 = {}
Expand Down
50 changes: 50 additions & 0 deletions synchro/migrations/0001_initial.py
Original file line number Diff line number Diff line change
@@ -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')]),
),
]
Empty file added synchro/migrations/__init__.py
Empty file.
12 changes: 7 additions & 5 deletions synchro/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from __future__ import absolute_import
import django
from django.contrib.admin.models import ADDITION, CHANGE, DELETION
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models
from django.utils.timezone import now
import dbsettings
import six


M2M_CHANGE = 4
Expand All @@ -24,24 +26,24 @@ 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')


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)
4 changes: 3 additions & 1 deletion synchro/settings.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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):
Expand Down
3 changes: 2 additions & 1 deletion synchro/signals.py
Original file line number Diff line number Diff line change
@@ -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')
Expand Down
1 change: 1 addition & 0 deletions synchro/test_urls.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# flake8: noqa
from __future__ import absolute_import
from django.contrib import admin
from django.conf.urls import url, include

Expand Down
11 changes: 6 additions & 5 deletions synchro/tests.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from __future__ import absolute_import
import datetime

from django import VERSION
Expand All @@ -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()
Expand Down Expand Up @@ -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."""
Expand Down
3 changes: 2 additions & 1 deletion synchro/urls.py
Original file line number Diff line number Diff line change
@@ -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 = (
Expand Down
12 changes: 7 additions & 5 deletions synchro/utility.py
Original file line number Diff line number Diff line change
@@ -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):
Expand All @@ -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:
Expand All @@ -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)

Expand Down Expand Up @@ -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):
Expand All @@ -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()
1 change: 1 addition & 0 deletions synchro/views.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down