From d48b62860caed906badec33d15d877dd0c9bd25e Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:12:37 +0500 Subject: [PATCH 1/6] Added python3 support and migrations (https://github.com/CogHealthTech/django-synchro) --- synchro/apps.py | 3 +- synchro/core.py | 7 +-- synchro/handlers.py | 5 ++- synchro/management/commands/synchronize.py | 9 ++-- synchro/migrations/0001_initial.py | 50 ++++++++++++++++++++++ synchro/migrations/__init__.py | 0 synchro/models.py | 12 +++--- synchro/settings.py | 4 +- synchro/signals.py | 3 +- synchro/test_urls.py | 1 + synchro/tests.py | 11 ++--- synchro/urls.py | 3 +- synchro/utility.py | 12 +++--- synchro/views.py | 1 + 14 files changed, 94 insertions(+), 27 deletions(-) create mode 100644 synchro/migrations/0001_initial.py create mode 100644 synchro/migrations/__init__.py 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 From aa4be1be5d7b12e83130366427a722388db5f9d1 Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:36:09 +0500 Subject: [PATCH 2/6] Switched to Django 3 --- synchro/management/commands/synchronize.py | 19 +++++++++++------ synchro/migrations/0001_initial.py | 24 +++++++++------------- synchro/models.py | 10 ++++----- synchro/utility.py | 3 ++- 4 files changed, 30 insertions(+), 26 deletions(-) diff --git a/synchro/management/commands/synchronize.py b/synchro/management/commands/synchronize.py index f34c71c..225fd96 100644 --- a/synchro/management/commands/synchronize.py +++ b/synchro/management/commands/synchronize.py @@ -10,7 +10,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 +from synchro.settings import REMOTE, LOCAL, SYNCHRO_CREATE_NEW import six @@ -73,22 +73,24 @@ def save_with_fks(ct, obj, new_pk): old_id = obj.pk obj._state.db = REMOTE - fks = (f for f in obj._meta.fields if f.rel) + fks = (f for f in obj._meta.fields if (f.many_to_one or f.one_to_one)) for f in fks: fk_id = f.value_from_object(obj) if fk_id is not None: - fk_ct = ContentType.objects.get_for_model(f.rel.to) + fk_ct = ContentType.objects.get_for_model(f.related_model()) rem, _ = ensure_exist(fk_ct, fk_id) f.save_form_data(obj, rem) obj.pk = new_pk obj.save(using=REMOTE) + r, n = Reference.objects.get_or_create(content_type=ct, local_object_id=old_id, defaults={'remote_object_id': obj.pk}) if not n and r.remote_object_id != obj.pk: r.remote_object_id = obj.pk r.save() + M2M_CACHE = {} @@ -189,7 +191,8 @@ def perform_add(ct, id, log=None): change_with_fks(ct, obj, rem) rem = obj else: - new_pk = None if obj._meta.has_auto_field else obj.pk + # new_pk = None if obj._meta.has_auto_field else obj.pk + new_pk = obj.pk create_with_fks(ct, obj, new_pk) rem = obj ref, _ = Reference.objects.get_or_create(content_type=ct, local_object_id=id, @@ -263,7 +266,11 @@ def synchronize(self, *args, **options): raise exception_class('No REMOTE database specified in settings.') since = app_options.last_check - last_time = datetime.now() + from django.utils import timezone + + last_time = timezone.now() + check_time = last_time + filters = {"date__gt": since} if since else {} logs = ChangeLog.objects.filter(**filters).select_related().order_by('date', 'pk') @@ -284,7 +291,7 @@ def synchronize(self, *args, **options): ACTIONS[log.action](log.content_type, log.object_id, log) if len(logs): - app_options.last_check = last_time + app_options.last_check = check_time return _t('Synchronization performed successfully.') else: return _t('No changes since last synchronization.') diff --git a/synchro/migrations/0001_initial.py b/synchro/migrations/0001_initial.py index 973de1d..df45c60 100644 --- a/synchro/migrations/0001_initial.py +++ b/synchro/migrations/0001_initial.py @@ -1,8 +1,5 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.11.15 on 2019-09-30 08:41 -from __future__ import unicode_literals +# Generated by Django 3.2.5 on 2022-06-20 11:58 -from __future__ import absolute_import from django.db import migrations, models import django.db.models.deletion @@ -19,32 +16,31 @@ class Migration(migrations.Migration): migrations.CreateModel( name='ChangeLog', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.BigAutoField(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')), + ('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')), + ('id', models.BigAutoField(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')), + ('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')), + ('id', models.BigAutoField(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')), + ('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')]), + options={ + 'unique_together': {('content_type', 'local_object_id')}, + }, ), ] diff --git a/synchro/models.py b/synchro/models.py index a8332f7..596d7fe 100644 --- a/synchro/models.py +++ b/synchro/models.py @@ -4,7 +4,7 @@ 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 +from django.utils import timezone import dbsettings import six @@ -20,12 +20,12 @@ class SynchroSettings(dbsettings.Group): - last_check = dbsettings.DateTimeValue('Last synchronization', default=now()) + last_check = dbsettings.DateTimeValue('Last synchronization', default=timezone.now) options = SynchroSettings() class Reference(models.Model): - content_type = models.ForeignKey(ContentType) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) local_object_id = models.CharField(max_length=256) remote_object_id = models.CharField(max_length=256) @@ -34,7 +34,7 @@ class Meta: class ChangeLog(models.Model): - content_type = models.ForeignKey(ContentType) + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.CharField(max_length=256) object = GenericForeignKey() date = models.DateTimeField(auto_now=True) @@ -45,5 +45,5 @@ def __unicode__(self): class DeleteKey(models.Model): - changelog = models.OneToOneField(ChangeLog) + changelog = models.OneToOneField(ChangeLog, on_delete=models.CASCADE) key = models.CharField(max_length=256) diff --git a/synchro/utility.py b/synchro/utility.py index 4460dc6..d6b37bd 100644 --- a/synchro/utility.py +++ b/synchro/utility.py @@ -1,5 +1,6 @@ from __future__ import absolute_import from datetime import datetime +from django.utils import timezone from django.core.exceptions import MultipleObjectsReturned, ValidationError from django.db.models import Manager, Model @@ -78,6 +79,6 @@ class Meta: def reset_synchro(): from .models import ChangeLog, Reference, options - options.last_check = datetime.now() + options.last_check = timezone.now() ChangeLog.objects.all().delete() Reference.objects.all().delete() From e9e4e793e4bf009951e75c5c4a27536835a08f68 Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:45:26 +0500 Subject: [PATCH 3/6] Only update object with same primary key, if the local modified object is newer than remotely modified object (needs synchro on remote db also ) --- synchro/management/commands/synchronize.py | 38 +++++++++++++++++----- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/synchro/management/commands/synchronize.py b/synchro/management/commands/synchronize.py index 225fd96..3ceaaa2 100644 --- a/synchro/management/commands/synchronize.py +++ b/synchro/management/commands/synchronize.py @@ -72,17 +72,37 @@ def save_with_fks(ct, obj, new_pk): """ old_id = obj.pk obj._state.db = REMOTE + rem = find_natural(ct, obj) + skip = False + if rem is not None: + if is_remote_newer(obj, rem): + skip = True - fks = (f for f in obj._meta.fields if (f.many_to_one or f.one_to_one)) - for f in fks: - fk_id = f.value_from_object(obj) - if fk_id is not None: - fk_ct = ContentType.objects.get_for_model(f.related_model()) - rem, _ = ensure_exist(fk_ct, fk_id) - f.save_form_data(obj, rem) + new_obj = obj.__class__.objects.filter(pk=obj.pk).using(REMOTE) - obj.pk = new_pk - obj.save(using=REMOTE) + if new_obj.exists(): + if not skip: + new_obj = new_obj[0] + values = obj.__dict__ + values.pop('_state') + if '_django_cleanup_original_cache' in values.keys(): + values.pop('_django_cleanup_original_cache') + obj.__class__.objects.filter(pk=obj.pk).using(REMOTE).update(**values) + + else: + fks = (f for f in obj._meta.fields if (f.many_to_one or f.one_to_one)) + for f in fks: + fk_id = f.value_from_object(obj) + if fk_id is not None: + fk_ct = ContentType.objects.get_for_model(f.related_model()) + rem, _ = ensure_exist(fk_ct, fk_id) + f.save_form_data(obj, rem) + + if not new_obj: + obj.pk = new_pk + else: + obj.pk = None + obj.save(using=REMOTE) r, n = Reference.objects.get_or_create(content_type=ct, local_object_id=old_id, defaults={'remote_object_id': obj.pk}) From 17aa68685673d817321187a5c23a64575de73ab9 Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:48:31 +0500 Subject: [PATCH 4/6] Added 'SYNCHRO_CREATE_NEW' settings for models that are created new (new pk) even if they already exist on remote servers (i.e Logs model) --- synchro/management/commands/synchronize.py | 7 ++++--- synchro/settings.py | 3 +++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/synchro/management/commands/synchronize.py b/synchro/management/commands/synchronize.py index 3ceaaa2..6b1009d 100644 --- a/synchro/management/commands/synchronize.py +++ b/synchro/management/commands/synchronize.py @@ -72,15 +72,16 @@ def save_with_fks(ct, obj, new_pk): """ old_id = obj.pk obj._state.db = REMOTE + new_pk_list = SYNCHRO_CREATE_NEW # Add models here that should not have the same primary key rem = find_natural(ct, obj) skip = False if rem is not None: if is_remote_newer(obj, rem): skip = True - new_obj = obj.__class__.objects.filter(pk=obj.pk).using(REMOTE) + new_obj= obj.__class__.objects.filter(pk=obj.pk).using(REMOTE) - if new_obj.exists(): + if new_obj.exists() and type(obj) not in new_pk_list: if not skip: new_obj = new_obj[0] values = obj.__dict__ @@ -98,7 +99,7 @@ def save_with_fks(ct, obj, new_pk): rem, _ = ensure_exist(fk_ct, fk_id) f.save_form_data(obj, rem) - if not new_obj: + if not new_obj and type(obj) not in new_pk_list: obj.pk = new_pk else: obj.pk = None diff --git a/synchro/settings.py b/synchro/settings.py index 16a43be..3166c7b 100644 --- a/synchro/settings.py +++ b/synchro/settings.py @@ -68,6 +68,9 @@ def prepare(): ALLOW_RESET = getattr(settings, 'SYNCHRO_ALLOW_RESET', True) DEBUG = getattr(settings, 'SYNCHRO_DEBUG', False) +# list of classes that will be created new while sync i.e Logs class +SYNCHRO_CREATE_NEW = getattr(settings, 'SYNCHRO_CREATE_NEW', []) + if REMOTE is None: if not hasattr(settings, 'SYNCHRO_REMOTE'): import warnings From 35429c449a18c6107d41286a65bb718dde1636f5 Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:49:04 +0500 Subject: [PATCH 5/6] Disable sync button if remote database is not configured --- synchro/templates/synchro.html | 5 +++-- synchro/views.py | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/synchro/templates/synchro.html b/synchro/templates/synchro.html index bbfe0d4..0c65bab 100644 --- a/synchro/templates/synchro.html +++ b/synchro/templates/synchro.html @@ -18,11 +18,12 @@

{% trans "Synchronization" %}

{% csrf_token %}
{% trans 'Last synchro time' %}: {{ last }}. -

+ +

{% if reset_allowed %}

+ onclick="return confirm(`All changes from last synchronization up to now will be forgotten and won't be synchronized in the future. Are you sure you want to proceed?`)"> {% endif %}
diff --git a/synchro/views.py b/synchro/views.py index d748437..d401186 100644 --- a/synchro/views.py +++ b/synchro/views.py @@ -26,4 +26,5 @@ def synchro(request): msg = _('Synchronization has been reset.') messages.add_message(request, messages.INFO, msg) return TemplateResponse(request, 'synchro.html', {'last': options.last_check, - 'reset_allowed': settings.ALLOW_RESET}) + 'reset_allowed': settings.ALLOW_RESET, + 'remote_db': settings.REMOTE}) From 120d2effc36f36a1c7c9a3370e130eb826963adc Mon Sep 17 00:00:00 2001 From: wahajmurtaza Date: Sun, 4 Sep 2022 17:53:23 +0500 Subject: [PATCH 6/6] Updated README --- README.rst | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.rst b/README.rst index 240a913..a4998cb 100644 --- a/README.rst +++ b/README.rst @@ -122,6 +122,13 @@ Generally, ``SYNCHRO_REMOTE`` setting can behave in 3 different ways: __ synchro_on_remote_ +``SYNCHRO_CREATE_NEW`` setting +-------------------------- +SYNCHRO_CREATE_NEW should contain list of classes that should not be updated on remote server instead if they are created +on local database and the same primary key is taken on remote database, they will be created new instead of update. + +This feature is created keeping in mind the models that can not be changed but only created new + (i.e Logs) Remarks and features ====================