diff --git a/fkreplace/__init__.py b/fkreplace/__init__.py index 0b554ef..7494cc3 100644 --- a/fkreplace/__init__.py +++ b/fkreplace/__init__.py @@ -1,5 +1,20 @@ +from django.core.exceptions import ObjectDoesNotExist -def merge(from_obj, to_obj): + +class MergeException(Exception): + pass + + +class OneToOneConflict(MergeException): + pass + + +ERROR = 0 +KEEP = 1 +UPDATE = 2 + + +def merge(from_obj, to_obj, one_to_one_conflict=ERROR): if not isinstance(from_obj, type(to_obj)): raise ValueError("both objects must be of the same type") @@ -17,17 +32,17 @@ def merge(from_obj, to_obj): try: field = getattr(from_obj, accessor_name) try: - getattr(to_obj, accessor_name) - except Exception as e: + to_obj_field = getattr(to_obj, accessor_name) + raise OneToOneConflict("both fields have an attribute set for {}".format(accessor_name)) + except ObjectDoesNotExist: # doesn't exist, safe to overwrite setattr(field, varname, to_obj) field.save() - except Exception as e: + except ObjectDoesNotExist: # from_obj one to one isn't set, skip pass else: - import pdb; pdb.set_trace() - raise Exception('unknown code path') + raise NotImplementedError('unexpected relation type, please file a bug') for related in from_obj._meta.get_all_related_many_to_many_objects(): accessor_name = related.get_accessor_name() diff --git a/tests/tests.py b/tests/tests.py index dfc0efd..3e2499c 100644 --- a/tests/tests.py +++ b/tests/tests.py @@ -1,6 +1,6 @@ from django.test import TestCase from .models import Person, Number, SSN, Group -from fkreplace import merge +from fkreplace import merge, OneToOneConflict class MergeTests(TestCase): def setUp(self): @@ -44,6 +44,12 @@ class MergeTests(TestCase): assert SSN.objects.count() == 1 # TODO: test one2one when there's a conflict + def test_one2one_conflict(self): + SSN.objects.create(person=self.a, number='1') + SSN.objects.create(person=self.b, number='1') + + with self.assertRaises(OneToOneConflict): + merge(self.a, self.b) def test_many2many_simple(self): self.g.people.add(self.a)