check unique collisions to prevent those objects from being restored

This commit is contained in:
Laura Klünder 2017-07-27 15:06:46 +02:00
parent bba5fab2ec
commit cf2496b5a7
3 changed files with 30 additions and 5 deletions

View file

@ -273,6 +273,25 @@ class ChangedObject(models.Model):
return False
return True
def get_unique_collisions(self, max_one=False):
result = set()
if not self.deleted:
return result
uniques = tuple(self.model_class._meta.unique_together)
uniques += tuple((field.name, )
for field in self.model_class._meta.get_fields() if field.unique and not field.primary_key)
for unique in uniques:
names = tuple((name if self.model_class._meta.get_field(name).related_model is None else name+'__pk')
for name in unique)
values = tuple(getattr(self.obj, self.model_class._meta.get_field(name).attname) for name in unique)
if None in values:
continue
if self.changeset.wrap_model(self.model_class).objects.filter(**dict(zip(names, values))).exists():
result |= set(unique)
if result and max_one:
return result
return result
def get_missing_dependencies(self, force_query=False, max_one=False):
result = set()
if not self.deleted:
@ -371,7 +390,7 @@ class ChangedObject(models.Model):
def restore(self):
if self.deleted is False:
return
if self.get_missing_dependencies(force_query=True, max_one=True):
if self.get_missing_dependencies(force_query=True, max_one=True) or self.get_unique_collisions(max_one=True):
raise PermissionError
self.deleted = False
self.save(standalone=True)

View file

@ -90,7 +90,7 @@
<a class="btn btn-default btn-xs pull-right" data-force-next-zoom href="{{ obj.edit_url }}">
{% trans 'Edit' %}
</a>
{% elif obj.deleted and can_edit and not obj.missing_dependencies %}
{% elif obj.deleted and can_edit and not obj.missing_dependencies and not obj.unique_collisions %}
<button type="submit" name="restore" value="{{ obj.pk }}" class="btn btn-warning btn-xs pull-right">
{% trans 'Restore' %}
</button>
@ -108,7 +108,7 @@
<tr>
<td class="text-{{ change.class }}"><i class="glyphicon glyphicon-{{ change.icon }}"></i></td>
<td{% if obj.deleted and change.icon != 'minus' %} class="text-muted"{% endif %}>
{% if change.missing_dependency %}<i class="glyphicon glyphicon-alert pull-right text-warning"></i>{% endif %}
{% if change.missing_dependency or change.unique_collision %}<i class="glyphicon glyphicon-alert pull-right text-warning"></i>{% endif %}
{% if change.empty %}<em>{% else %}<u>{% endif %}{{ change.title }}{% if not change.empty %}</u>:{% else %}</em>{% endif %}
{{ change.value }}
</td>

View file

@ -48,8 +48,8 @@ def changeset_detail(request, pk):
changed_object.restore()
messages.success(request, _('Object has been successfully restored.'))
except PermissionError:
messages.error(request, _('You cannot restore this object, because '
'it depends on a deleted object.'))
messages.error(request, _('You cannot restore this object, because it depends on '
'a deleted object or it would violate a unique contraint.'))
else:
messages.error(request, _('You can not edit changes on this change set.'))
@ -234,6 +234,7 @@ def changeset_detail(request, pk):
changes = []
missing_dependencies = changed_object.get_missing_dependencies()
unique_collisions = changed_object.get_unique_collisions()
changed_object_data = {
'model': obj.__class__,
'model_title': obj.__class__._meta.verbose_name,
@ -244,6 +245,7 @@ def changeset_detail(request, pk):
'edit_url': edit_url,
'deleted': changed_object.deleted,
'missing_dependencies': missing_dependencies,
'unique_collisions': unique_collisions,
'order': (changed_object.deleted and changed_object.is_created, not changed_object.is_created),
}
changed_objects_data.append(changed_object_data)
@ -293,6 +295,10 @@ def changeset_detail(request, pk):
change_data.update({
'missing_dependency': field.name in missing_dependencies,
})
if name in unique_collisions:
change_data.update({
'unique_collision': field.name in unique_collisions,
})
order = 5
if name == 'slug':
order = 1