django-reorder-items-widget 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_reorder_items_widget-1.0.0/LICENCE +27 -0
- django_reorder_items_widget-1.0.0/PKG-INFO +131 -0
- django_reorder_items_widget-1.0.0/README.md +97 -0
- django_reorder_items_widget-1.0.0/django_reorder_items_widget.egg-info/PKG-INFO +131 -0
- django_reorder_items_widget-1.0.0/django_reorder_items_widget.egg-info/SOURCES.txt +18 -0
- django_reorder_items_widget-1.0.0/django_reorder_items_widget.egg-info/dependency_links.txt +1 -0
- django_reorder_items_widget-1.0.0/django_reorder_items_widget.egg-info/requires.txt +1 -0
- django_reorder_items_widget-1.0.0/django_reorder_items_widget.egg-info/top_level.txt +1 -0
- django_reorder_items_widget-1.0.0/pyproject.toml +51 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/__init__.py +0 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/__version__.py +16 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/apps.py +6 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/static/css/reorder-items-widget.css +9 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/static/js/reorder-items-widget.js +136 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/templates/forms/widgets/reorder-items-widget.html +14 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/templatetags/__init__.py +0 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/templatetags/reorder_items_widget.py +9 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/tests.py +28 -0
- django_reorder_items_widget-1.0.0/reorder_items_widget/widgets.py +20 -0
- django_reorder_items_widget-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
Copyright (c) 2024, Thomas Leichtfuß.
|
|
2
|
+
All rights reserved.
|
|
3
|
+
|
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
|
5
|
+
are permitted provided that the following conditions are met:
|
|
6
|
+
|
|
7
|
+
1. Redistributions of source code must retain the above copyright notice,
|
|
8
|
+
this list of conditions and the following disclaimer.
|
|
9
|
+
|
|
10
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
|
12
|
+
documentation and/or other materials provided with the distribution.
|
|
13
|
+
|
|
14
|
+
3. Neither the name of the author nor the names of contributors
|
|
15
|
+
may be used to endorse or promote products derived from this software
|
|
16
|
+
without specific prior written permission.
|
|
17
|
+
|
|
18
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
19
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
20
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
21
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
|
|
22
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
23
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
24
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
25
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
27
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-reorder-items-widget
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Easily reorder the order of items within the django admin backend.
|
|
5
|
+
Author-email: Thomas Leichtfuß <thomas.leichtfuss@posteo.de>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/thomst/django-reorder-items-widget
|
|
8
|
+
Project-URL: Repository, https://github.com/thomst/django-reorder-items-widget
|
|
9
|
+
Project-URL: Documentation, https://github.com/thomst/django-reorder-items-widget#readme
|
|
10
|
+
Keywords: django,django-admin,widgets
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Framework :: Django :: 3.2
|
|
14
|
+
Classifier: Framework :: Django :: 4.0
|
|
15
|
+
Classifier: Framework :: Django :: 4.1
|
|
16
|
+
Classifier: Framework :: Django :: 4.2
|
|
17
|
+
Classifier: Framework :: Django :: 5.0
|
|
18
|
+
Classifier: Framework :: Django :: 5.1
|
|
19
|
+
Classifier: Framework :: Django :: 5.2
|
|
20
|
+
Classifier: Framework :: Django :: 6.0
|
|
21
|
+
Classifier: Environment :: Web Environment
|
|
22
|
+
Classifier: Intended Audience :: Developers
|
|
23
|
+
Classifier: Operating System :: OS Independent
|
|
24
|
+
Classifier: Programming Language :: Python
|
|
25
|
+
Classifier: Programming Language :: Python :: 3
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
27
|
+
Classifier: Topic :: Software Development
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENCE
|
|
32
|
+
Requires-Dist: Django>=3.2
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# Welcome to django-reorder-items-widget
|
|
36
|
+
|
|
37
|
+
[](https://github.com/thomst/django-reorder-items-widget/actions/workflows/tests.yml)
|
|
38
|
+
[<img src="https://coveralls.io/repos/github/thomst/django-reorder-items-widget/badge.svg?branch=main">](https://coveralls.io/github/thomst/django-reorder-items-widget?branch=main)
|
|
39
|
+
[<img src="https://img.shields.io/badge/python-3-blue">](https://img.shields.io/badge/python-3-blue)
|
|
40
|
+
[<img src="https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange">](https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Description
|
|
44
|
+
|
|
45
|
+
Reorder your items by simply dragging them to their new position. This works
|
|
46
|
+
fine within django admin's changelists or inline model forms.
|
|
47
|
+
|
|
48
|
+
All you need to do is to use an editable index field with the famous
|
|
49
|
+
`ReorderItemsWidget` of this app.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
Install via pip:
|
|
55
|
+
```
|
|
56
|
+
pip install django-reorder-items-widget
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Setup
|
|
60
|
+
|
|
61
|
+
Add `reorder_items_widget` to your `INSTALLED_APPS`:
|
|
62
|
+
```
|
|
63
|
+
INSTALLED_APPS = [
|
|
64
|
+
'reorder_items_widget',
|
|
65
|
+
...
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Add a index field to your model:
|
|
70
|
+
```
|
|
71
|
+
class Item(models.Model):
|
|
72
|
+
index = models.PositiveSmallIntegerField()
|
|
73
|
+
...
|
|
74
|
+
class Meta:
|
|
75
|
+
ordering = ('index',)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Using the ReorderItemsWidget with your index field only makes sense in result
|
|
79
|
+
lists or inline modeladmin forms. A simple way to put the widget in place is a
|
|
80
|
+
custom model form:
|
|
81
|
+
```
|
|
82
|
+
class ReorderItemForm(forms.ModelForm):
|
|
83
|
+
class Meta:
|
|
84
|
+
widgets={'index': ReorderItemsWidget()}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Now you can use this form with your changelist by overwriting the
|
|
88
|
+
`get_changelist_form` method of your model admin:
|
|
89
|
+
```
|
|
90
|
+
class BaseItemAdmin(admin.ModelAdmin):
|
|
91
|
+
list_editable = ("index",)
|
|
92
|
+
list_display = ("index", ...)
|
|
93
|
+
|
|
94
|
+
def get_changelist_form(self, request, **kwargs):
|
|
95
|
+
kwargs.setdefault('form', ReorderItemForm)
|
|
96
|
+
return super().get_changelist_form(request, **kwargs)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**_NOTE:_** Mind that your index field must be editable.
|
|
100
|
+
|
|
101
|
+
To use the widget with your inline modeladmin simple at your form to the
|
|
102
|
+
`TabularInline` class:
|
|
103
|
+
```
|
|
104
|
+
class ItemInline(admin.TabularInline):
|
|
105
|
+
form = ReorderItemForm
|
|
106
|
+
fields = ("index", ...)
|
|
107
|
+
...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
That's it.
|
|
111
|
+
|
|
112
|
+
## Caveats
|
|
113
|
+
|
|
114
|
+
The widget will always number your items sequentially. Reordering items in a
|
|
115
|
+
filtered list might be have unexpected results. Paging however should not be a
|
|
116
|
+
problem since indexes are updated using the lowest one as base.
|
|
117
|
+
|
|
118
|
+
## General considerations on switching index values
|
|
119
|
+
|
|
120
|
+
There is a general problem with switching values on a unique index field: In
|
|
121
|
+
mysql like databases you will run into a constraint violation - even if you
|
|
122
|
+
update all items in a single update transaction.
|
|
123
|
+
|
|
124
|
+
To work around this you can either obmit the unique constraint. Or implement a
|
|
125
|
+
complex saving logic like saving changed indexes as negative values first and
|
|
126
|
+
update them to their positiv counterpart afterwards.
|
|
127
|
+
|
|
128
|
+
## Contribute
|
|
129
|
+
|
|
130
|
+
Feedback, feature requests, bug reports or pull requests are most welcome. Just
|
|
131
|
+
use the common github infrastructure.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Welcome to django-reorder-items-widget
|
|
2
|
+
|
|
3
|
+
[](https://github.com/thomst/django-reorder-items-widget/actions/workflows/tests.yml)
|
|
4
|
+
[<img src="https://coveralls.io/repos/github/thomst/django-reorder-items-widget/badge.svg?branch=main">](https://coveralls.io/github/thomst/django-reorder-items-widget?branch=main)
|
|
5
|
+
[<img src="https://img.shields.io/badge/python-3-blue">](https://img.shields.io/badge/python-3-blue)
|
|
6
|
+
[<img src="https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange">](https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
## Description
|
|
10
|
+
|
|
11
|
+
Reorder your items by simply dragging them to their new position. This works
|
|
12
|
+
fine within django admin's changelists or inline model forms.
|
|
13
|
+
|
|
14
|
+
All you need to do is to use an editable index field with the famous
|
|
15
|
+
`ReorderItemsWidget` of this app.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
Install via pip:
|
|
21
|
+
```
|
|
22
|
+
pip install django-reorder-items-widget
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Setup
|
|
26
|
+
|
|
27
|
+
Add `reorder_items_widget` to your `INSTALLED_APPS`:
|
|
28
|
+
```
|
|
29
|
+
INSTALLED_APPS = [
|
|
30
|
+
'reorder_items_widget',
|
|
31
|
+
...
|
|
32
|
+
]
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Add a index field to your model:
|
|
36
|
+
```
|
|
37
|
+
class Item(models.Model):
|
|
38
|
+
index = models.PositiveSmallIntegerField()
|
|
39
|
+
...
|
|
40
|
+
class Meta:
|
|
41
|
+
ordering = ('index',)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Using the ReorderItemsWidget with your index field only makes sense in result
|
|
45
|
+
lists or inline modeladmin forms. A simple way to put the widget in place is a
|
|
46
|
+
custom model form:
|
|
47
|
+
```
|
|
48
|
+
class ReorderItemForm(forms.ModelForm):
|
|
49
|
+
class Meta:
|
|
50
|
+
widgets={'index': ReorderItemsWidget()}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Now you can use this form with your changelist by overwriting the
|
|
54
|
+
`get_changelist_form` method of your model admin:
|
|
55
|
+
```
|
|
56
|
+
class BaseItemAdmin(admin.ModelAdmin):
|
|
57
|
+
list_editable = ("index",)
|
|
58
|
+
list_display = ("index", ...)
|
|
59
|
+
|
|
60
|
+
def get_changelist_form(self, request, **kwargs):
|
|
61
|
+
kwargs.setdefault('form', ReorderItemForm)
|
|
62
|
+
return super().get_changelist_form(request, **kwargs)
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**_NOTE:_** Mind that your index field must be editable.
|
|
66
|
+
|
|
67
|
+
To use the widget with your inline modeladmin simple at your form to the
|
|
68
|
+
`TabularInline` class:
|
|
69
|
+
```
|
|
70
|
+
class ItemInline(admin.TabularInline):
|
|
71
|
+
form = ReorderItemForm
|
|
72
|
+
fields = ("index", ...)
|
|
73
|
+
...
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
That's it.
|
|
77
|
+
|
|
78
|
+
## Caveats
|
|
79
|
+
|
|
80
|
+
The widget will always number your items sequentially. Reordering items in a
|
|
81
|
+
filtered list might be have unexpected results. Paging however should not be a
|
|
82
|
+
problem since indexes are updated using the lowest one as base.
|
|
83
|
+
|
|
84
|
+
## General considerations on switching index values
|
|
85
|
+
|
|
86
|
+
There is a general problem with switching values on a unique index field: In
|
|
87
|
+
mysql like databases you will run into a constraint violation - even if you
|
|
88
|
+
update all items in a single update transaction.
|
|
89
|
+
|
|
90
|
+
To work around this you can either obmit the unique constraint. Or implement a
|
|
91
|
+
complex saving logic like saving changed indexes as negative values first and
|
|
92
|
+
update them to their positiv counterpart afterwards.
|
|
93
|
+
|
|
94
|
+
## Contribute
|
|
95
|
+
|
|
96
|
+
Feedback, feature requests, bug reports or pull requests are most welcome. Just
|
|
97
|
+
use the common github infrastructure.
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-reorder-items-widget
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Easily reorder the order of items within the django admin backend.
|
|
5
|
+
Author-email: Thomas Leichtfuß <thomas.leichtfuss@posteo.de>
|
|
6
|
+
License: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/thomst/django-reorder-items-widget
|
|
8
|
+
Project-URL: Repository, https://github.com/thomst/django-reorder-items-widget
|
|
9
|
+
Project-URL: Documentation, https://github.com/thomst/django-reorder-items-widget#readme
|
|
10
|
+
Keywords: django,django-admin,widgets
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
12
|
+
Classifier: Framework :: Django
|
|
13
|
+
Classifier: Framework :: Django :: 3.2
|
|
14
|
+
Classifier: Framework :: Django :: 4.0
|
|
15
|
+
Classifier: Framework :: Django :: 4.1
|
|
16
|
+
Classifier: Framework :: Django :: 4.2
|
|
17
|
+
Classifier: Framework :: Django :: 5.0
|
|
18
|
+
Classifier: Framework :: Django :: 5.1
|
|
19
|
+
Classifier: Framework :: Django :: 5.2
|
|
20
|
+
Classifier: Framework :: Django :: 6.0
|
|
21
|
+
Classifier: Environment :: Web Environment
|
|
22
|
+
Classifier: Intended Audience :: Developers
|
|
23
|
+
Classifier: Operating System :: OS Independent
|
|
24
|
+
Classifier: Programming Language :: Python
|
|
25
|
+
Classifier: Programming Language :: Python :: 3
|
|
26
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
|
27
|
+
Classifier: Topic :: Software Development
|
|
28
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
29
|
+
Requires-Python: >=3.8
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
License-File: LICENCE
|
|
32
|
+
Requires-Dist: Django>=3.2
|
|
33
|
+
Dynamic: license-file
|
|
34
|
+
|
|
35
|
+
# Welcome to django-reorder-items-widget
|
|
36
|
+
|
|
37
|
+
[](https://github.com/thomst/django-reorder-items-widget/actions/workflows/tests.yml)
|
|
38
|
+
[<img src="https://coveralls.io/repos/github/thomst/django-reorder-items-widget/badge.svg?branch=main">](https://coveralls.io/github/thomst/django-reorder-items-widget?branch=main)
|
|
39
|
+
[<img src="https://img.shields.io/badge/python-3-blue">](https://img.shields.io/badge/python-3-blue)
|
|
40
|
+
[<img src="https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange">](https://img.shields.io/badge/django-3.2%20%7C%204.0%20%7C%204.1%20%7C%204.2%20%7C%205.0%20%7C%205.1%20%7C%205.2%20%7C%206.0-orange)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## Description
|
|
44
|
+
|
|
45
|
+
Reorder your items by simply dragging them to their new position. This works
|
|
46
|
+
fine within django admin's changelists or inline model forms.
|
|
47
|
+
|
|
48
|
+
All you need to do is to use an editable index field with the famous
|
|
49
|
+
`ReorderItemsWidget` of this app.
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
## Installation
|
|
53
|
+
|
|
54
|
+
Install via pip:
|
|
55
|
+
```
|
|
56
|
+
pip install django-reorder-items-widget
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Setup
|
|
60
|
+
|
|
61
|
+
Add `reorder_items_widget` to your `INSTALLED_APPS`:
|
|
62
|
+
```
|
|
63
|
+
INSTALLED_APPS = [
|
|
64
|
+
'reorder_items_widget',
|
|
65
|
+
...
|
|
66
|
+
]
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Add a index field to your model:
|
|
70
|
+
```
|
|
71
|
+
class Item(models.Model):
|
|
72
|
+
index = models.PositiveSmallIntegerField()
|
|
73
|
+
...
|
|
74
|
+
class Meta:
|
|
75
|
+
ordering = ('index',)
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Using the ReorderItemsWidget with your index field only makes sense in result
|
|
79
|
+
lists or inline modeladmin forms. A simple way to put the widget in place is a
|
|
80
|
+
custom model form:
|
|
81
|
+
```
|
|
82
|
+
class ReorderItemForm(forms.ModelForm):
|
|
83
|
+
class Meta:
|
|
84
|
+
widgets={'index': ReorderItemsWidget()}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Now you can use this form with your changelist by overwriting the
|
|
88
|
+
`get_changelist_form` method of your model admin:
|
|
89
|
+
```
|
|
90
|
+
class BaseItemAdmin(admin.ModelAdmin):
|
|
91
|
+
list_editable = ("index",)
|
|
92
|
+
list_display = ("index", ...)
|
|
93
|
+
|
|
94
|
+
def get_changelist_form(self, request, **kwargs):
|
|
95
|
+
kwargs.setdefault('form', ReorderItemForm)
|
|
96
|
+
return super().get_changelist_form(request, **kwargs)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**_NOTE:_** Mind that your index field must be editable.
|
|
100
|
+
|
|
101
|
+
To use the widget with your inline modeladmin simple at your form to the
|
|
102
|
+
`TabularInline` class:
|
|
103
|
+
```
|
|
104
|
+
class ItemInline(admin.TabularInline):
|
|
105
|
+
form = ReorderItemForm
|
|
106
|
+
fields = ("index", ...)
|
|
107
|
+
...
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
That's it.
|
|
111
|
+
|
|
112
|
+
## Caveats
|
|
113
|
+
|
|
114
|
+
The widget will always number your items sequentially. Reordering items in a
|
|
115
|
+
filtered list might be have unexpected results. Paging however should not be a
|
|
116
|
+
problem since indexes are updated using the lowest one as base.
|
|
117
|
+
|
|
118
|
+
## General considerations on switching index values
|
|
119
|
+
|
|
120
|
+
There is a general problem with switching values on a unique index field: In
|
|
121
|
+
mysql like databases you will run into a constraint violation - even if you
|
|
122
|
+
update all items in a single update transaction.
|
|
123
|
+
|
|
124
|
+
To work around this you can either obmit the unique constraint. Or implement a
|
|
125
|
+
complex saving logic like saving changed indexes as negative values first and
|
|
126
|
+
update them to their positiv counterpart afterwards.
|
|
127
|
+
|
|
128
|
+
## Contribute
|
|
129
|
+
|
|
130
|
+
Feedback, feature requests, bug reports or pull requests are most welcome. Just
|
|
131
|
+
use the common github infrastructure.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENCE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
django_reorder_items_widget.egg-info/PKG-INFO
|
|
5
|
+
django_reorder_items_widget.egg-info/SOURCES.txt
|
|
6
|
+
django_reorder_items_widget.egg-info/dependency_links.txt
|
|
7
|
+
django_reorder_items_widget.egg-info/requires.txt
|
|
8
|
+
django_reorder_items_widget.egg-info/top_level.txt
|
|
9
|
+
reorder_items_widget/__init__.py
|
|
10
|
+
reorder_items_widget/__version__.py
|
|
11
|
+
reorder_items_widget/apps.py
|
|
12
|
+
reorder_items_widget/tests.py
|
|
13
|
+
reorder_items_widget/widgets.py
|
|
14
|
+
reorder_items_widget/static/css/reorder-items-widget.css
|
|
15
|
+
reorder_items_widget/static/js/reorder-items-widget.js
|
|
16
|
+
reorder_items_widget/templates/forms/widgets/reorder-items-widget.html
|
|
17
|
+
reorder_items_widget/templatetags/__init__.py
|
|
18
|
+
reorder_items_widget/templatetags/reorder_items_widget.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Django>=3.2
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
reorder_items_widget
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "django-reorder-items-widget"
|
|
7
|
+
authors = [{name = "Thomas Leichtfuß", email = "thomas.leichtfuss@posteo.de"}]
|
|
8
|
+
description = "Easily reorder the order of items within the django admin backend."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.8"
|
|
11
|
+
keywords = ["django", "django-admin", "widgets"]
|
|
12
|
+
license = { text = "BSD-3-Clause"}
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 5 - Production/Stable",
|
|
15
|
+
"Framework :: Django",
|
|
16
|
+
"Framework :: Django :: 3.2",
|
|
17
|
+
"Framework :: Django :: 4.0",
|
|
18
|
+
"Framework :: Django :: 4.1",
|
|
19
|
+
"Framework :: Django :: 4.2",
|
|
20
|
+
"Framework :: Django :: 5.0",
|
|
21
|
+
"Framework :: Django :: 5.1",
|
|
22
|
+
"Framework :: Django :: 5.2",
|
|
23
|
+
"Framework :: Django :: 6.0",
|
|
24
|
+
"Environment :: Web Environment",
|
|
25
|
+
"Intended Audience :: Developers",
|
|
26
|
+
"Operating System :: OS Independent",
|
|
27
|
+
"Programming Language :: Python",
|
|
28
|
+
"Programming Language :: Python :: 3",
|
|
29
|
+
"Topic :: Internet :: WWW/HTTP :: Dynamic Content",
|
|
30
|
+
"Topic :: Software Development",
|
|
31
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
32
|
+
]
|
|
33
|
+
dependencies = [
|
|
34
|
+
"Django>=3.2",
|
|
35
|
+
]
|
|
36
|
+
dynamic = ["version"]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/thomst/django-reorder-items-widget"
|
|
40
|
+
Repository = "https://github.com/thomst/django-reorder-items-widget"
|
|
41
|
+
Documentation = "https://github.com/thomst/django-reorder-items-widget#readme"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools]
|
|
44
|
+
packages = ["reorder_items_widget", "reorder_items_widget.templatetags"]
|
|
45
|
+
include-package-data = false
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.package-data]
|
|
48
|
+
reorder_items_widget = ["templates/**", "static/**"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.dynamic]
|
|
51
|
+
version = {attr = "reorder_items_widget.__version__.__version__"}
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This project uses the Semantic Versioning scheme in conjunction with PEP 0440:
|
|
3
|
+
<http://semver.org/>
|
|
4
|
+
<https://www.python.org/dev/peps/pep-0440>
|
|
5
|
+
|
|
6
|
+
Major versions introduce significant changes to the API, and backwards
|
|
7
|
+
compatibility is not guaranteed. Minor versions are for new features and other
|
|
8
|
+
backwards-compatible changes to the API. Patch versions are for bug fixes and
|
|
9
|
+
internal code changes that do not affect the API. Development versions are
|
|
10
|
+
incomplete states of a release .
|
|
11
|
+
|
|
12
|
+
Version 0.x should be considered a development version with an unstable API,
|
|
13
|
+
and backwards compatibility is not guaranteed for minor versions.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
class ReorderItemsTable {
|
|
5
|
+
|
|
6
|
+
constructor (table) {
|
|
7
|
+
this.table = table;
|
|
8
|
+
this.form = table.closest('form');
|
|
9
|
+
this.baseIndex = Number(table.querySelector('.reorder-items-widget-index').value);
|
|
10
|
+
const style = window.getComputedStyle(table.querySelector('tbody > tr'))
|
|
11
|
+
this.rowHeight = style.getPropertyValue('height');
|
|
12
|
+
this.initRows();
|
|
13
|
+
this.initHeaderAsDragOverAndDropZone();
|
|
14
|
+
this.initFormSubmission();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
getRows() {
|
|
18
|
+
var selector;
|
|
19
|
+
if (this.table.id == 'result_list') {
|
|
20
|
+
selector = 'tbody > tr';
|
|
21
|
+
} else {
|
|
22
|
+
selector = 'tbody > tr.has_original'
|
|
23
|
+
}
|
|
24
|
+
return this.table.querySelectorAll(selector);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
initRows() {
|
|
28
|
+
[].forEach.call(this.getRows(), (row) => {
|
|
29
|
+
this.initRowAsDragOver(row);
|
|
30
|
+
this.initRowAsDropZone(row);
|
|
31
|
+
this.initRowAsDraggable(row);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
initRowAsDragOver(row) {
|
|
36
|
+
var counter = 0
|
|
37
|
+
const that = this;
|
|
38
|
+
row.addEventListener("dragenter", function (e) {
|
|
39
|
+
counter++;
|
|
40
|
+
// The hovering row is not itself or the one above - which also
|
|
41
|
+
// could be the header.
|
|
42
|
+
const firstRow = that.table.querySelector('tbody > tr');
|
|
43
|
+
if (
|
|
44
|
+
!row.draggable &&
|
|
45
|
+
!(row.nextElementSibling && row.nextElementSibling.draggable) &&
|
|
46
|
+
!(row.parentElement.nodeName == 'THEAD' && firstRow.draggable)
|
|
47
|
+
) that.addDragOverStyle(row);
|
|
48
|
+
});
|
|
49
|
+
row.addEventListener("dragleave", function (e) {
|
|
50
|
+
counter--;
|
|
51
|
+
if (counter == 0)
|
|
52
|
+
that.removeDragOverStyle(row);
|
|
53
|
+
});
|
|
54
|
+
row.addEventListener("drop", function (e) {
|
|
55
|
+
counter = 0;
|
|
56
|
+
that.removeDragOverStyle(row);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
addDragOverStyle(row) {
|
|
61
|
+
row.style.borderBottom = `${this.rowHeight} solid var(--selected-bg)`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
removeDragOverStyle(row) {
|
|
65
|
+
row.style.borderBottom = 0;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
initRowAsDropZone(row) {
|
|
69
|
+
row.addEventListener("dragover", function (e) {
|
|
70
|
+
e.preventDefault();
|
|
71
|
+
});
|
|
72
|
+
row.addEventListener("drop", function (e) {
|
|
73
|
+
e.preventDefault();
|
|
74
|
+
const inputID = e.dataTransfer.getData("text");
|
|
75
|
+
const draggedRow = document.getElementById(inputID).closest('tr');
|
|
76
|
+
row.after(draggedRow);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
initRowAsDraggable(row) {
|
|
81
|
+
const handle = row.querySelector('.drag-handle');
|
|
82
|
+
handle.addEventListener("mousedown", function (e) {
|
|
83
|
+
row.setAttribute('draggable', 'true');
|
|
84
|
+
});
|
|
85
|
+
handle.addEventListener("mouseup", function(e) {
|
|
86
|
+
row.setAttribute('draggable', 'false');
|
|
87
|
+
});
|
|
88
|
+
row.addEventListener("dragstart", function (e) {
|
|
89
|
+
row.classList.add("on-drag");
|
|
90
|
+
const inputID = row.querySelector('input.reorder-items-widget-index').id;
|
|
91
|
+
e.dataTransfer.setData("text", inputID);
|
|
92
|
+
});
|
|
93
|
+
row.addEventListener("dragend", function (e) {
|
|
94
|
+
row.classList.remove("on-drag");
|
|
95
|
+
row.setAttribute('draggable', 'false');
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
initHeaderAsDragOverAndDropZone() {
|
|
100
|
+
const row = this.table.querySelector('thead > tr');
|
|
101
|
+
this.initRowAsDragOver(row);
|
|
102
|
+
row.addEventListener("dragover", function (e) {
|
|
103
|
+
e.preventDefault();
|
|
104
|
+
});
|
|
105
|
+
const querySelector = this.table.querySelector.bind(this.table);
|
|
106
|
+
row.addEventListener("drop", function (e) {
|
|
107
|
+
e.preventDefault();
|
|
108
|
+
const inputID = e.dataTransfer.getData("text");
|
|
109
|
+
const draggedRow = document.getElementById(inputID).closest('tr');
|
|
110
|
+
const firstRow = querySelector('tbody > tr');
|
|
111
|
+
firstRow.before(draggedRow);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
initFormSubmission() {
|
|
116
|
+
const that = this;
|
|
117
|
+
this.form.addEventListener("submit", function (e) {
|
|
118
|
+
[].forEach.call(that.getRows(), (row, counter) => {
|
|
119
|
+
const index = that.baseIndex + counter;
|
|
120
|
+
row.querySelector('.reorder-items-widget-index').value = index;
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
document.addEventListener("DOMContentLoaded", function () {
|
|
128
|
+
// Init each table which uses the reorder-items-widget.
|
|
129
|
+
document.querySelectorAll('table').forEach((el) => {
|
|
130
|
+
if (el.querySelector('.drag-handle')) {
|
|
131
|
+
new ReorderItemsTable(el);
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
})();
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{% load reorder_items_widget %}
|
|
2
|
+
|
|
3
|
+
{% comment %}
|
|
4
|
+
We only render the widget for existing items. Doing something smart with the
|
|
5
|
+
index input for extra rows in inline model forms - like using a hidden input and
|
|
6
|
+
set the index within a pre-save signal handler - is not our buisness.
|
|
7
|
+
{% endcomment %}
|
|
8
|
+
|
|
9
|
+
{% if widget.value %}
|
|
10
|
+
{% hide_widget %}
|
|
11
|
+
<div title="Drag me!" class="drag-handle">⇳</div>
|
|
12
|
+
{% endif %}
|
|
13
|
+
|
|
14
|
+
{% include "django/forms/widgets/input.html" %}
|
|
File without changes
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
from django.test import TestCase
|
|
2
|
+
from .widgets import ReorderItemsWidget
|
|
3
|
+
|
|
4
|
+
# Just for coverage score.
|
|
5
|
+
from . import __version__
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class WidgetTestCase(TestCase):
|
|
9
|
+
def test_widget(self):
|
|
10
|
+
widget = ReorderItemsWidget()
|
|
11
|
+
css_classes = widget.build_attrs(dict(), dict())['class']
|
|
12
|
+
self.assertIn('reorder-items-widget-index', css_classes)
|
|
13
|
+
|
|
14
|
+
def test_rendering_with_value(self):
|
|
15
|
+
widget = ReorderItemsWidget()
|
|
16
|
+
html = widget.render('index', 1)
|
|
17
|
+
input = '<input type="number" name="index" value="1" class="reorder-items-widget-index hidden">'
|
|
18
|
+
drag_handle = '<div title="Drag me!" class="drag-handle">⇳</div>'
|
|
19
|
+
self.assertInHTML(input, html)
|
|
20
|
+
self.assertInHTML(drag_handle, html)
|
|
21
|
+
|
|
22
|
+
def test_rendering_without_value(self):
|
|
23
|
+
widget = ReorderItemsWidget()
|
|
24
|
+
html = widget.render('index', None)
|
|
25
|
+
input = '<input type="number" name="index" class="reorder-items-widget-index">'
|
|
26
|
+
drag_handle = '<div title="Drag me!" class="drag-handle">⇳</div>'
|
|
27
|
+
self.assertInHTML(input, html)
|
|
28
|
+
self.assertNotIn(drag_handle, html)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from django.forms import NumberInput
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class ReorderItemsWidget(NumberInput):
|
|
5
|
+
"""
|
|
6
|
+
A widget that allows to move items up and down within a listing with an
|
|
7
|
+
editable index field.
|
|
8
|
+
"""
|
|
9
|
+
template_name = 'forms/widgets/reorder-items-widget.html'
|
|
10
|
+
|
|
11
|
+
class Media:
|
|
12
|
+
css = {'all': ('css/reorder-items-widget.css',)}
|
|
13
|
+
js = ('js/reorder-items-widget.js',)
|
|
14
|
+
|
|
15
|
+
def build_attrs(self, base_attrs, extra_attrs):
|
|
16
|
+
attrs = super().build_attrs(base_attrs, extra_attrs)
|
|
17
|
+
css_classes = attrs['class'].split(' ') if attrs.get('class') else []
|
|
18
|
+
css_classes += ['reorder-items-widget-index']
|
|
19
|
+
attrs['class'] = ' '.join(css_classes)
|
|
20
|
+
return attrs
|