Skip to content

Commit a4e5460

Browse files
committed
closes issue #81
1 parent af394e0 commit a4e5460

File tree

6 files changed

+90
-1
lines changed

6 files changed

+90
-1
lines changed

CHANGES

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
Release 1.4 (dev)
22
-----------------
3+
* closes :issue:`81`. Add docs and check.
34
* fixes :issue:`80`. (thanks Naddiseo for the useful support)
45
* Django 1.11 compatibility
56
* some minor support for Django 2.0

docs/api.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ ConcurrentModelAdmin
7575
--------------------
7676
.. autoclass:: concurrency.admin.ConcurrentModelAdmin
7777

78+
.. warning:: If you customize ``fields`` or ``fieldsets`` remember to add version field to the list. (See issue :ghissue:`81`)
79+
80+
81+
7882
.. _ConcurrencyActionMixin:
7983

8084
ConcurrencyActionMixin

docs/conf.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@
5858

5959
}
6060

61+
github_project_url = 'https://github.com/saxix/django-concurrency'
62+
6163
todo_include_todos = True
6264

6365
# Add any paths that contain templates here, relative to this directory.

src/concurrency/admin.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
from django.contrib import admin, messages
99
from django.contrib.admin import helpers
10+
from django.core.checks import Error
1011
from django.core.exceptions import ImproperlyConfigured, ValidationError
1112
from django.db.models import Q
1213
from django.forms.formsets import (
@@ -23,6 +24,7 @@
2324
from concurrency.config import CONCURRENCY_LIST_EDITABLE_POLICY_ABORT_ALL, conf
2425
from concurrency.exceptions import RecordModifiedError
2526
from concurrency.forms import ConcurrentForm, VersionWidget
27+
from concurrency.utils import flatten
2628

2729
ALL = object()
2830

@@ -251,3 +253,31 @@ class ConcurrentModelAdmin(ConcurrencyActionMixin,
251253
admin.ModelAdmin):
252254
form = ConcurrentForm
253255
formfield_overrides = {forms.VersionField: {'widget': VersionWidget}}
256+
257+
def check(self, **kwargs):
258+
errors = []
259+
if self.fields:
260+
version_field = self.model._concurrencymeta.field
261+
if version_field.name not in self.fields:
262+
errors.append(
263+
Error(
264+
'Missed version field in {} fields definition'.format(self),
265+
hint="Please add '{}' to the 'fields' attribute".format(version_field.name),
266+
obj=None,
267+
id='concurrency.A001',
268+
)
269+
)
270+
if self.fieldsets:
271+
version_field = self.model._concurrencymeta.field
272+
fields = flatten([v['fields'] for k, v in self.fieldsets])
273+
274+
if version_field.name not in fields:
275+
errors.append(
276+
Error(
277+
'Missed version field in {} fieldsets definition'.format(self),
278+
hint="Please add '{}' to the 'fieldsets' attribute".format(version_field.name),
279+
obj=None,
280+
id='concurrency.A002',
281+
)
282+
)
283+
return errors

src/concurrency/utils.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# -*- coding: utf-8 -*-
22
from __future__ import absolute_import, unicode_literals
3-
3+
import six
44
import inspect
55
import logging
66
import warnings
@@ -183,3 +183,32 @@ def fqn(o):
183183
if not parts:
184184
raise ValueError("Invalid argument `%s`" % o)
185185
return ".".join(parts)
186+
187+
188+
def flatten(iterable):
189+
"""
190+
flatten(sequence) -> list
191+
192+
Returns a single, flat list which contains all elements retrieved
193+
from the sequence and all recursively contained sub-sequences
194+
(iterables).
195+
196+
:param sequence: any object that implements iterable protocol (see: :ref:`typeiter`)
197+
:return: list
198+
199+
Examples:
200+
201+
>>> from adminactions.utils import flatten
202+
>>> [1, 2, [3,4], (5,6)]
203+
[1, 2, [3, 4], (5, 6)]
204+
205+
>>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, (8,9,10)])
206+
[1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]"""
207+
208+
result = list()
209+
for el in iterable:
210+
if hasattr(el, "__iter__") and not isinstance(el, six.string_types):
211+
result.extend(flatten(el))
212+
else:
213+
result.append(el)
214+
return list(result)

tests/test_issues.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
import pytest
66
from django.contrib.admin.sites import site
77
from django.contrib.auth.models import User
8+
from django.core.management import call_command
9+
from django.core.management.base import SystemCheckError
810
from django.http import QueryDict
911
from django.test import override_settings
1012
from django.test.client import RequestFactory
@@ -111,3 +113,24 @@ def test_issue_54():
111113

112114
with pytest.raises(RecordModifiedError):
113115
m2.save()
116+
117+
118+
@pytest.mark.django_db()
119+
def test_issue_81a(monkeypatch):
120+
monkeypatch.setattr('demo.admin.ActionsModelAdmin.fields', ('id',))
121+
with pytest.raises(SystemCheckError) as e:
122+
call_command('check')
123+
assert 'concurrency.A001' in e.value.message
124+
125+
126+
@pytest.mark.django_db()
127+
def test_issue_81b(monkeypatch):
128+
fieldsets = (
129+
('Standard info', {
130+
'fields': ('id',)
131+
}),
132+
)
133+
monkeypatch.setattr('demo.admin.ActionsModelAdmin.fieldsets', fieldsets)
134+
with pytest.raises(SystemCheckError) as e:
135+
call_command('check')
136+
assert 'concurrency.A002' in e.value.message

0 commit comments

Comments
 (0)