ConcurrencyMiddlewareΒΆ
You can globally intercept RecordModifiedError
adding ConcurrencyMiddleware
to your MIDDLEWARE_CLASSES
.
Each time a RecordModifiedError
is raised it goes up to the ConcurrencyMiddleware and the handler defined in
CONCURRENCY_HANDLER409
is invoked.
Example
settings.py
MIDDLEWARE_CLASSES=('django.middleware.common.CommonMiddleware',
'concurrency.middleware.ConcurrencyMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware')
CONCURRENCY_HANDLER409 = 'demoproject.demoapp.views.conflict'
CONCURRENCY_POLICY = 2 # CONCURRENCY_LIST_EDITABLE_POLICY_ABORT_ALL
views.py
from diff_match_patch import diff_match_patch
from concurrency.views import ConflictResponse
from django.template import loader
from django.utils.safestring import mark_safe
from django.template.context import RequestContext
def get_diff(current, stored):
data = []
dmp = diff_match_patch()
fields = current._meta.fields
for field in fields:
v1 = getattr(current, field.name, "")
v2 = getattr(stored, field.name, "")
diff = dmp.diff_main(unicode(v1), unicode(v2))
dmp.diff_cleanupSemantic(diff)
html = dmp.diff_prettyHtml(diff)
html = mark_safe(html)
data.append((field, v1, v2, html))
return data
def conflict(request, target=None, template_name='409.html'):
template = loader.get_template(template_name)
try:
saved = target.__class__._default_manager.get(pk=target.pk)
diff = get_diff(target, saved)
except target.__class__.DoesNotExists:
saved = None
diff = None
ctx = RequestContext(request, {'target': target,
'diff': diff,
'saved': saved,
'request_path': request.path})
return ConflictResponse(template.render(ctx))
409.html
{% load concurrency %}
<table>
<tr>
<th>
Field
</th>
<th>
Current
</th>
<th>
Stored
</th>
<th>
Diff
</th>
</tr>
<tr>
{% for field, current, stored, entry in diff %}
{% if not field.primary_key and not field|is_version %}
<tr>
<td>
{{ field.verbose_name }}
</td>
<td>
{{ current }}
</td>
<td>
{{ stored }}
</td>
<td>
{{ entry }}
</td>
</tr>
{% endif %}
{% endfor %}
</tr>
</table>
If you want to use ConcurrentMiddleware in the admin and you are using
concurrency.admin.ConcurrentModelAdmin
remember to set your ModelAdmin to NOT
use concurrency.forms.ConcurrentForm
from django import forms
class MyModelAdmin(ConcurrentModelAdmin):
form = forms.ModelForm # overrides default ConcurrentForm