From f4cb942fa42e62fdbff76a60553e40dfad975614 Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Wed, 11 Dec 2013 17:38:08 +0100 Subject: [PATCH] release 1.0 --- LICENSE | 27 ++++++++++ README | 24 +++++++++ django_sync/__init__.py | 0 django_sync/management/__init__.py | 0 django_sync/management/commands/__init__.py | 0 .../management/commands/sync-with-remote.py | 53 +++++++++++++++++++ django_sync/models.py | 3 ++ django_sync/tests.py | 16 ++++++ django_sync/views.py | 1 + setup.py | 17 ++++++ 10 files changed, 141 insertions(+) create mode 100644 LICENSE create mode 100644 README create mode 100644 django_sync/__init__.py create mode 100644 django_sync/management/__init__.py create mode 100644 django_sync/management/commands/__init__.py create mode 100644 django_sync/management/commands/sync-with-remote.py create mode 100644 django_sync/models.py create mode 100644 django_sync/tests.py create mode 100644 django_sync/views.py create mode 100755 setup.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f27d51a --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) Entr'ouvert +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. Neither the name of Django nor the names of its contributors may be used + to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README b/README new file mode 100644 index 0000000..044325d --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +Install +------- + +Add application to your INSTALLED_APPS:: + + INSTALLED_APPS += ('django_sync',) + +Add a remote DB to your DATABASES setting:: + + DATABASES['remote'] = { + 'remote': { + 'ENGINE': 'django.db.backends.postgresql_psycopg2', + 'NAME': 'remote', + }, + +Usage +----- + +./manage.py sync-with-remote [--from=remote] [--to=default] + +Synchronize models in order from the remote db to the default db, or other db +if specified. You must order your models so that no model at the start refer to +model at the end of the list, through a foreign key field or a many to many +field. diff --git a/django_sync/__init__.py b/django_sync/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_sync/management/__init__.py b/django_sync/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_sync/management/commands/__init__.py b/django_sync/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_sync/management/commands/sync-with-remote.py b/django_sync/management/commands/sync-with-remote.py new file mode 100644 index 0000000..7edb521 --- /dev/null +++ b/django_sync/management/commands/sync-with-remote.py @@ -0,0 +1,53 @@ +from optparse import make_option + +from django.core.management.base import BaseCommand, CommandError + +from django.db import transaction, connections +from django.db.models.loading import get_model + +class Command(BaseCommand): + args = '' + help = 'Synchronize models between DBs' + option_list = BaseCommand.option_list + ( + make_option('--from', + default='remote', + help='Database id to pull objects from', + ), + make_option('--to', + default='default', + help='Database id to push objects to', + ), + ) + + def handle(self, *args, **options): + models = [] + for arg in args: + try: + app_label, model_name = arg.split('.', 1) + except ValueError: + raise CommandError('invalid model name %s' % model) + models.append(get_model(app_label, model_name)) + db_from = options['from'] + db_to = options['to'] + try: + connections[db_from] + except KeyError: + raise CommandError('unknown db %s' % db_from) + try: + connections[db_to] + except KeyError: + raise CommandError('unknown db %s' % db_to) + + with transaction.commit_on_success(db_to): + connections[db_to].cursor().execute('SET CONSTRAINTS ALL DEFERRED') + while models: + model, models = models[0], models[1:] + opts = model._meta + qs_from = model.objects.using(db_from).all() + for instance in qs_from: + instance.save(using=db_to) + for many_to_many_field in opts.many_to_many: + through_model = getattr(model, many_to_many_field.name).through + models.insert(0, through_model) + qs_to = model.objects.using(db_to).exclude(id__in=list(qs_from.values_list('id', flat=True))) + qs_to.delete() diff --git a/django_sync/models.py b/django_sync/models.py new file mode 100644 index 0000000..71a8362 --- /dev/null +++ b/django_sync/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/django_sync/tests.py b/django_sync/tests.py new file mode 100644 index 0000000..501deb7 --- /dev/null +++ b/django_sync/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/django_sync/views.py b/django_sync/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/django_sync/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..4751270 --- /dev/null +++ b/setup.py @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +from setuptools import setup, find_packages + +setup(name="django-sync", + version='1.0', + license="BSD", + description="Synchronize django models between DBs", + author="Entr'ouvert", + author_email="info@entrouvert.org", + maintainer="Benjamin Dauvergne", + maintainer_email="info@entrouvert.com", + include_package_data=True, + packages=find_packages(), + install_requires=[], + dependency_links=[], +)