Skip to content

Commit

Permalink
feat(generic): introduce a generic merge view
Browse files Browse the repository at this point in the history
This view shows the values before and after merge instead of simply
merging.
  • Loading branch information
b1rger committed Sep 24, 2024
1 parent b0e5294 commit 5f21c4d
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 1 deletion.
4 changes: 4 additions & 0 deletions apis_core/generic/abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@ def get_delete_url(self):
ct = ContentType.objects.get_for_model(self)
return reverse("apis_core:generic:delete", args=[ct, self.id])

def get_merge_url(self, other_id):
ct = ContentType.objects.get_for_model(self)
return reverse("apis_core:generic:merge", args=[ct, self.id, other_id])

def get_create_success_url(self):
return self.get_absolute_url()

Expand Down
7 changes: 7 additions & 0 deletions apis_core/generic/forms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,10 @@ def __init__(self, *args, **kwargs):
url, attrs={"data-html": True}
)
self.fields[field].widget.choices = self.fields[field].choices


class GenericMergeForm(forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.add_input(Submit("submit", "Merge"))
42 changes: 42 additions & 0 deletions apis_core/generic/templates/generic/generic_merge.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{% extends basetemplate|default:"base.html" %}
{% load apisgeneric %}
{% load apis_history_templatetags %}
{% load crispy_forms_tags %}
{% load static %}

{% block styles %}
{{ block.super }}
<link rel="stylesheet" href="{% static 'css/history.css' %}" />
{% endblock styles %}

{% block content %}
<div class="container">
<h4 class="mb-4">
Merging values of <a href="{{ other.get_absolute_url }}">{{ other }}</a> into <a href="{{ object.get_absolute_url }}">{{ object }}</a>
</h4>
<div class="alert alert-warning" role="alert">
After the merge <a href="{{ other.get_absolute_url }}">{{ other }}</a> will be deleted!
</div>
<table class="table table-sm table-hover table-bordered difftable">
<thead>
<tr>
<th></th>
<th>Old value</th>
<th>New value</th>
</tr>
</thead>
<tbody>
{% for change in changes %}
<tr>
<th>{{ change.field }}</th>
<td>{{ change|get_diff_old }}</td>
<td>{{ change|get_diff_new }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if form %}
{% crispy form form.helper %}
{% endif %}
</div>
{% endblock %}
5 changes: 5 additions & 0 deletions apis_core/generic/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ def to_url(self, value):
path("create", views.Create.as_view(), name="create"),
path("delete/<int:pk>", views.Delete.as_view(), name="delete"),
path("update/<int:pk>", views.Update.as_view(), name="update"),
path(
"merge/<int:pk>/<int:otherpk>",
views.MergeWith.as_view(),
name="merge",
),
path("autocomplete", views.Autocomplete.as_view(), name="autocomplete"),
path("import", views.Import.as_view(), name="import"),
path(
Expand Down
47 changes: 46 additions & 1 deletion apis_core/generic/views.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
from collections import namedtuple

from dal import autocomplete
from django import forms, http
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.forms import modelform_factory
from django.shortcuts import get_object_or_404
from django.template.exceptions import TemplateDoesNotExist
from django.template.loader import select_template
from django.urls import reverse, reverse_lazy
Expand All @@ -19,7 +23,7 @@
from apis_core.utils.helpers import create_object_from_uri

from .filtersets import GenericFilterSet
from .forms import GenericImportForm, GenericModelForm
from .forms import GenericImportForm, GenericMergeForm, GenericModelForm
from .helpers import (
first_member_match,
generate_search_filter,
Expand Down Expand Up @@ -331,3 +335,44 @@ def form_valid(self, form):

def get_success_url(self):
return self.object.get_absolute_url()


class MergeWith(GenericModelMixin, PermissionRequiredMixin, FormView):
"""
Generic merge view.
"""

permission_action_required = "change"
form_class = GenericMergeForm
template_name = "generic/generic_merge.html"

def setup(self, *args, **kwargs):
super().setup(*args, **kwargs)
self.object = get_object_or_404(self.model, pk=self.kwargs["pk"])
self.other = get_object_or_404(self.model, pk=self.kwargs["otherpk"])

def get_context_data(self, **kwargs):
"""
The context consists of the two objects that are merged as well
as a list of changes. Those changes are presented in the view as
a table with diffs
"""
Change = namedtuple("Change", "field old new")
ctx = super().get_context_data(**kwargs)
ctx["changes"] = []
for field in self.object._meta.fields:
newval = self.object.get_field_value_after_merge(self.other, field)
ctx["changes"].append(
Change(field.verbose_name, getattr(self.object, field.name), newval)
)
ctx["object"] = self.object
ctx["other"] = self.other
return ctx

def form_valid(self, form):
self.object.merge_with([self.other])
messages.info(self.request, f"Merged values of {self.other} into {self.object}")
return super().form_valid(form)

def get_success_url(self):
return self.object.get_absolute_url()

0 comments on commit 5f21c4d

Please sign in to comment.