def test_transaction_merge(users): master, other = users with mock.patch('django.contrib.auth.models.User.delete', side_effect=IntegrityError): with pytest.raises(IntegrityError): merge(master, other, commit=True) assert User.objects.filter(pk=master.pk).exists() assert User.objects.filter(pk=other.pk).exists() assert master.first_name != other.first_name
def test_merge_success_m2m_all(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) group = Group.objects.get_or_create(name='G1')[0] perm = Permission.objects.all()[0] other.groups.add(group) other.user_permissions.add(perm) other.save() merge(master, other, commit=True, m2m=ALL_FIELDS) self.assertSequenceEqual(master.groups.all(), [group]) self.assertSequenceEqual(master.user_permissions.all(), [perm])
def test_merge_success_related_all(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) entry = other.logentry_set.get_or_create(object_repr='test', action_flag=1)[0] result = merge(master, other, commit=True, related=ALL_FIELDS) master = User.objects.get(pk=result.pk) # reload self.assertSequenceEqual(master.logentry_set.all(), [entry]) self.assertTrue(LogEntry.objects.filter(pk=entry.pk).exists())
def test_merge_ignore_related(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) entry = other.logentry_set.get_or_create(object_repr='test', action_flag=1)[0] result = merge(master, other, commit=True, related=None) master = User.objects.get(pk=result.pk) # reload self.assertSequenceEqual(master.logentry_set.all(), []) self.assertFalse(User.objects.filter(pk=other.pk).exists()) self.assertFalse(LogEntry.objects.filter(pk=entry.pk).exists())
def test_merge_success_m2m(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) group = Group.objects.get_or_create(name='G1')[0] other.groups.add(group) other.save() result = merge(master, other, commit=True, m2m=['groups']) master = User.objects.get(pk=result.pk) # reload self.assertSequenceEqual(master.groups.all(), [group])
def test_merge_success_no_commit(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) result = merge(master, other) self.assertTrue(User.objects.filter(pk=master.pk).exists()) self.assertTrue(User.objects.filter(pk=other.pk).exists()) self.assertEqual(result.pk, master.pk) self.assertEqual(result.first_name, other.first_name) self.assertEqual(result.last_name, other.last_name) self.assertEqual(result.password, other.password)
def test_merge_one_to_one_move_single(self): master = DemoModel.objects.get(pk=1) other = DemoModel.objects.get(pk=2) related_one = DemoOneToOne(demo=other) related_one.save() result = merge(master, other, commit=True, related=ALL_FIELDS) master = DemoModel.objects.get(pk=result.pk) # reload self.assertEqual(master.onetoone, related_one) self.assertTrue(DemoOneToOne.objects.filter(pk=related_one.pk).exists()) self.assertEqual(os.path.basename(master.image.file.name), "first.png")
def test_merge_success_no_commit(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) result = merge(master, other) self.assertTrue(User.objects.filter(pk=master.pk).exists()) self.assertTrue(User.objects.filter(pk=other.pk).exists()) self.assertEqual(result.pk, master.pk) self.assertEqual(result.first_name, master.first_name) self.assertEqual(result.last_name, master.last_name) self.assertEqual(result.password, master.password)
def test_merge_one_to_one_field(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) profile = assert_profile(other) if profile: entry = other.logentry_set.get_or_create(object_repr='test', action_flag=1)[0] result = merge(master, other, commit=True, related=ALL_FIELDS) master = User.objects.get(pk=result.pk) # reload self.assertSequenceEqual(master.logentry_set.all(), [entry]) self.assertTrue(LogEntry.objects.filter(pk=entry.pk).exists()) self.assertEqual(get_profile(result), profile)
def test_merge_one_to_one_field(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) profile = get_profile(other) if profile: entry = other.logentry_set.get_or_create(object_repr='test', action_flag=1)[0] result = merge(master, other, commit=True, related=ALL_FIELDS) master = User.objects.get(pk=result.pk) # reload self.assertSequenceEqual(master.logentry_set.all(), [entry]) self.assertTrue(LogEntry.objects.filter(pk=entry.pk).exists()) self.assertEqual(get_profile(result), profile)
def test_merge_success_commit(self): old_master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) result = merge(old_master, other, commit=True) master = User.objects.get(pk=result.pk) # reload self.assertTrue(User.objects.filter(pk=master.pk).exists()) self.assertFalse(User.objects.filter(pk=other.pk).exists()) self.assertEqual(result.pk, master.pk) self.assertEqual(master.first_name, old_master.first_name) self.assertEqual(master.last_name, old_master.last_name) self.assertEqual(master.password, old_master.password)
def test_merge_success_fields_no_commit(self): master = User.objects.get(pk=self.master_pk) other = User.objects.get(pk=self.other_pk) result = merge(master, other, ['password', 'last_login']) master = User.objects.get(pk=master.pk) self.assertTrue(User.objects.filter(pk=master.pk).exists()) self.assertTrue(User.objects.filter(pk=other.pk).exists()) self.assertNotEqual(result.last_login, master.last_login) self.assertEqual(result.last_login, other.last_login) self.assertEqual(result.password, other.password) self.assertNotEqual(result.last_name, other.last_name)
def test_merge_image(self): master = DemoModel.objects.get(pk=3) other = DemoModel.objects.get(pk=1) img1 = other.image img2 = other.subclassed_image assert master.image != other.image assert master.subclassed_image != other.subclassed_image result = merge(master, other, fields=['image', 'subclassed_image'], commit=True, related=None) master = DemoModel.objects.get(pk=result.pk) # reload self.assertFalse(DemoModel.objects.filter(pk=other.pk).exists()) self.assertEqual(master.image, img1) self.assertEqual(master.subclassed_image, img2)
def merge(modeladmin, request, queryset): """ Merge two model instances. Move all foreign keys. """ opts = modeladmin.model._meta perm = "{0}.{1}".format( opts.app_label.lower(), get_permission_codename('adminactions_merge', opts)) if not request.user.has_perm(perm): messages.error( request, _('Sorry you do not have rights to execute this action (%s)' % perm)) return def raw_widget(field, **kwargs): """ force all fields as not required""" kwargs['widget'] = TextInput({'class': 'raw-value'}) return field.formfield(**kwargs) merge_form = getattr(modeladmin, 'merge_form', MergeForm) MForm = modelform_factory(modeladmin.model, form=merge_form, exclude=('pk', ), formfield_callback=raw_widget) OForm = modelform_factory(modeladmin.model, exclude=('pk', ), formfield_callback=raw_widget) tpl = 'adminactions/merge.html' # transaction_supported = model_supports_transactions(modeladmin.model) ctx = { '_selected_action': request.POST.getlist(helpers.ACTION_CHECKBOX_NAME), 'transaction_supported': 'Un', 'select_across': request.POST.get('select_across') == '1', 'action': request.POST.get('action'), 'fields': [ f for f in queryset.model._meta.fields if not f.primary_key and f.editable ], 'app_label': queryset.model._meta.app_label, 'result': '', 'opts': queryset.model._meta } if 'preview' in request.POST: master = queryset.get(pk=request.POST.get('master_pk')) original = clone_instance(master) other = queryset.get(pk=request.POST.get('other_pk')) formset = formset_factory(OForm)( initial=[model_to_dict(master), model_to_dict(other)]) with transaction.nocommit(): form = MForm(request.POST, instance=master) other.delete() form_is_valid = form.is_valid() if form_is_valid: ctx.update({'original': original}) tpl = 'adminactions/merge_preview.html' else: master = queryset.get(pk=request.POST.get('master_pk')) other = queryset.get(pk=request.POST.get('other_pk')) elif 'apply' in request.POST: master = queryset.get(pk=request.POST.get('master_pk')) other = queryset.get(pk=request.POST.get('other_pk')) formset = formset_factory(OForm)( initial=[model_to_dict(master), model_to_dict(other)]) with transaction.nocommit(): form = MForm(request.POST, instance=master) stored_pk = other.pk other.delete() ok = form.is_valid() other.pk = stored_pk if ok: if form.cleaned_data['dependencies'] == MergeForm.DEP_MOVE: related = api.ALL_FIELDS else: related = None fields = form.cleaned_data['field_names'] api.merge(master, other, fields=fields, commit=True, related=related) return HttpResponseRedirect(request.path) else: messages.error(request, form.errors) else: try: master, other = queryset.all() # django 1.4 need to remove the trailing milliseconds for field in master._meta.fields: if isinstance(field, models.DateTimeField): for target in (master, other): raw_value = getattr(target, field.name) fixed_value = datetime(raw_value.year, raw_value.month, raw_value.day, raw_value.hour, raw_value.minute, raw_value.second) setattr(target, field.name, fixed_value) except ValueError: messages.error(request, _('Please select exactly 2 records')) return initial = { '_selected_action': request.POST.getlist(helpers.ACTION_CHECKBOX_NAME), 'select_across': 0, 'generic': MergeForm.GEN_IGNORE, 'dependencies': MergeForm.DEP_MOVE, 'action': 'merge', 'master_pk': master.pk, 'other_pk': other.pk } formset = formset_factory(OForm)( initial=[model_to_dict(master), model_to_dict(other)]) form = MForm(initial=initial, instance=master) adminForm = helpers.AdminForm(form, modeladmin.get_fieldsets(request), {}, [], model_admin=modeladmin) media = modeladmin.media + adminForm.media ctx.update({ 'adminform': adminForm, 'formset': formset, 'media': mark_safe(media), 'title': u"Merge %s" % force_unicode(modeladmin.opts.verbose_name_plural), 'master': master, 'other': other }) return render_to_response(tpl, RequestContext(request, ctx))
def merge(modeladmin, request, queryset): # noqa """ Merge two model instances. Move all foreign keys. """ opts = modeladmin.model._meta perm = "{0}.{1}".format(opts.app_label, get_permission_codename('adminactions_merge', opts)) if not request.user.has_perm(perm): messages.error(request, _('Sorry you do not have rights to execute this action (%s)' % perm)) return def raw_widget(field, **kwargs): """ force all fields as not required""" kwargs['widget'] = TextInput({'class': 'raw-value'}) return field.formfield(**kwargs) merge_form = getattr(modeladmin, 'merge_form', MergeForm) MForm = modelform_factory(modeladmin.model, form=merge_form, exclude=('pk', ), formfield_callback=raw_widget) OForm = modelform_factory(modeladmin.model, exclude=('pk', ), formfield_callback=raw_widget) tpl = 'adminactions/merge.html' # transaction_supported = model_supports_transactions(modeladmin.model) ctx = { '_selected_action': request.POST.getlist(helpers.ACTION_CHECKBOX_NAME), 'transaction_supported': 'Un', 'select_across': request.POST.get('select_across') == '1', 'action': request.POST.get('action'), 'fields': [f for f in queryset.model._meta.fields if not f.primary_key and f.editable], 'app_label': queryset.model._meta.app_label, 'result': '', 'opts': queryset.model._meta} if 'preview' in request.POST: master = queryset.get(pk=request.POST.get('master_pk')) original = clone_instance(master) other = queryset.get(pk=request.POST.get('other_pk')) formset = formset_factory(OForm)(initial=[model_to_dict(master), model_to_dict(other)]) with transaction.nocommit(): form = MForm(request.POST, instance=master) other.delete() form_is_valid = form.is_valid() if form_is_valid: ctx.update({'original': original}) tpl = 'adminactions/merge_preview.html' else: master = queryset.get(pk=request.POST.get('master_pk')) other = queryset.get(pk=request.POST.get('other_pk')) elif 'apply' in request.POST: master = queryset.get(pk=request.POST.get('master_pk')) other = queryset.get(pk=request.POST.get('other_pk')) formset = formset_factory(OForm)(initial=[model_to_dict(master), model_to_dict(other)]) with transaction.nocommit(): form = MForm(request.POST, instance=master) stored_pk = other.pk other.delete() ok = form.is_valid() other.pk = stored_pk if ok: if form.cleaned_data['dependencies'] == MergeForm.DEP_MOVE: related = api.ALL_FIELDS else: related = None fields = form.cleaned_data['field_names'] api.merge(master, other, fields=fields, commit=True, related=related) return HttpResponseRedirect(request.path) else: messages.error(request, form.errors) else: try: master, other = queryset.all() # django 1.4 need to remove the trailing milliseconds for field in master._meta.fields: if isinstance(field, models.DateTimeField): for target in (master, other): raw_value = getattr(target, field.name) fixed_value = datetime(raw_value.year, raw_value.month, raw_value.day, raw_value.hour, raw_value.minute, raw_value.second) setattr(target, field.name, fixed_value) except ValueError: messages.error(request, _('Please select exactly 2 records')) return initial = {'_selected_action': request.POST.getlist(helpers.ACTION_CHECKBOX_NAME), 'select_across': 0, 'generic': MergeForm.GEN_IGNORE, 'dependencies': MergeForm.DEP_MOVE, 'action': 'merge', 'master_pk': master.pk, 'other_pk': other.pk} formset = formset_factory(OForm)(initial=[model_to_dict(master), model_to_dict(other)]) form = MForm(initial=initial, instance=master) adminForm = helpers.AdminForm(form, modeladmin.get_fieldsets(request), {}, [], model_admin=modeladmin) media = modeladmin.media + adminForm.media ctx.update({'adminform': adminForm, 'formset': formset, 'media': mark_safe(media), 'title': u"Merge %s" % smart_text(modeladmin.opts.verbose_name_plural), 'master': master, 'other': other}) return render_to_response(tpl, RequestContext(request, ctx))