parent
5ad2c0e4ec
commit
612de9ea9d
70
README.md
70
README.md
|
|
@ -100,7 +100,6 @@ class MySortableClass(SortableMixin):
|
||||||
ordering = ['the_order']
|
ordering = ['the_order']
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# define the field the model should be ordered by
|
# define the field the model should be ordered by
|
||||||
the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
||||||
|
|
||||||
|
|
@ -429,6 +428,71 @@ which can make them difficult to sort. If you anticipate the height of a
|
||||||
stacked inline is going to be very tall, I would suggest using
|
stacked inline is going to be very tall, I would suggest using
|
||||||
SortableTabularInline instead.
|
SortableTabularInline instead.
|
||||||
|
|
||||||
|
#### Custom JS callbacks after sorting is complete
|
||||||
|
If you need to define a custom event or other callback to be executed after sorting is completed, you'll need to:
|
||||||
|
|
||||||
|
1. Create a custom template for to add your JavaScript
|
||||||
|
2. Populate the `after_sorting_js_callback_name` on your model admin
|
||||||
|
|
||||||
|
An example of this can be found in the "samples" application in the source. Here's a model admin for a model called "Project":
|
||||||
|
|
||||||
|
```python
|
||||||
|
class ProjectAdmin(SortableAdmin):
|
||||||
|
inlines = [
|
||||||
|
CreditInline, NoteInline, GenericNoteInline,
|
||||||
|
NonSortableCreditInline, NonSortableNoteInline
|
||||||
|
]
|
||||||
|
list_display = ['__str__', 'category']
|
||||||
|
|
||||||
|
after_sorting_js_callback_name = 'afterSortCallback' # do not include () - just function name
|
||||||
|
sortable_change_list_template = 'adminsortable/custom_change_list.html'
|
||||||
|
sortable_change_form_template = "adminsortable/custom_change_form.html"
|
||||||
|
```
|
||||||
|
|
||||||
|
This example is going to add a custom callback on the parent model, and it's inlines. Here is the JavaScript added to the custom change list:
|
||||||
|
|
||||||
|
```html+django
|
||||||
|
{% extends 'adminsortable/change_list.html' %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
django.jQuery(document).on('order:changed', function(event) {
|
||||||
|
console.log(event.message);
|
||||||
|
// your code here
|
||||||
|
});
|
||||||
|
|
||||||
|
window['{{ after_sorting_js_callback_name }}'] = function() {
|
||||||
|
django.jQuery(document).trigger({ type: 'order:changed', message: 'Order changed', time: new Date() });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
```
|
||||||
|
|
||||||
|
and the custom change form, for the inline models:
|
||||||
|
|
||||||
|
```html+django
|
||||||
|
{% extends "adminsortable/change_form.html" %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
django.jQuery(document).on('order:changed', function(event) {
|
||||||
|
console.log(event.message);
|
||||||
|
// your code here
|
||||||
|
});
|
||||||
|
|
||||||
|
window['{{ after_sorting_js_callback_name }}'] = function() {
|
||||||
|
django.jQuery(document).trigger({ type: 'order:changed', message: 'Order changed', time: new Date() });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
```
|
||||||
|
|
||||||
|
Ideally, you'd pull in a shared piece of code for your callback to keep your code DRY.
|
||||||
|
|
||||||
### Django-CMS integration
|
### Django-CMS integration
|
||||||
Django-CMS plugins use their own change form, and thus won't automatically
|
Django-CMS plugins use their own change form, and thus won't automatically
|
||||||
include the necessary JavaScript for django-admin-sortable to work. Fortunately,
|
include the necessary JavaScript for django-admin-sortable to work. Fortunately,
|
||||||
|
|
@ -531,8 +595,8 @@ ordering on top of that just seemed a little much in my opinion.
|
||||||
### Status
|
### Status
|
||||||
django-admin-sortable is currently used in production.
|
django-admin-sortable is currently used in production.
|
||||||
|
|
||||||
### What's new in 2.1.5?
|
### What's new in 2.1.6?
|
||||||
- Support for Django Admin filters. Credit to [timur-orudzhov](https://github.com/timur-orudzhov).
|
- Added inclusion of custom JavaScript callbacks after sorting is performed, if desired.
|
||||||
|
|
||||||
### Future
|
### Future
|
||||||
- Better template support for foreign keys that are self referential. If someone would like to take on rendering recursive sortables, that would be super.
|
- Better template support for foreign keys that are self referential. If someone would like to take on rendering recursive sortables, that would be super.
|
||||||
|
|
|
||||||
427
README.rst
427
README.rst
|
|
@ -1,7 +1,9 @@
|
||||||
Django Admin Sortable
|
Django Admin Sortable
|
||||||
=====================
|
=====================
|
||||||
|
|
||||||
|PyPI version| |Python versions| |Build Status|
|
`PyPI version <https://pypi.python.org/pypi/django-admin-sortable>`__
|
||||||
|
`Python versions <https://pypi.python.org/pypi/django-admin-sortable>`__
|
||||||
|
`Build Status <https://travis-ci.org/alsoicode/django-admin-sortable>`__
|
||||||
|
|
||||||
This project makes it easy to add drag-and-drop ordering to any model in
|
This project makes it easy to add drag-and-drop ordering to any model in
|
||||||
Django admin. Inlines for a sortable model may also be made sortable,
|
Django admin. Inlines for a sortable model may also be made sortable,
|
||||||
|
|
@ -124,24 +126,23 @@ Sample Model:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# models.py
|
# models.py
|
||||||
from adminsortable.models import SortableMixin
|
from adminsortable.models import SortableMixin
|
||||||
|
|
||||||
class MySortableClass(SortableMixin):
|
class MySortableClass(SortableMixin):
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = 'My Sortable Class'
|
verbose_name = 'My Sortable Class'
|
||||||
verbose_name_plural = 'My Sortable Classes'
|
verbose_name_plural = 'My Sortable Classes'
|
||||||
ordering = ['the_order']
|
ordering = ['the_order']
|
||||||
|
|
||||||
|
|
||||||
|
# define the field the model should be ordered by
|
||||||
|
the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
||||||
|
|
||||||
# define the field the model should be ordered by
|
def __unicode__(self):
|
||||||
the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
return self.title
|
||||||
|
|
||||||
def __unicode__(self):
|
|
||||||
return self.title
|
|
||||||
|
|
||||||
Support for models that don’t use an ``AutoField`` for their primary key
|
Support for models that don’t use an ``AutoField`` for their primary key
|
||||||
are also supported in version 2.0.20 or higher.
|
are also supported in version 2.0.20 or higher.
|
||||||
|
|
@ -155,39 +156,39 @@ set up your models and admin options:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# models.py
|
# models.py
|
||||||
from adminsortable.fields import SortableForeignKey
|
from adminsortable.fields import SortableForeignKey
|
||||||
|
|
||||||
class Category(SortableMixin):
|
class Category(SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['category_order']
|
ordering = ['category_order']
|
||||||
verbose_name_plural = 'Categories'
|
verbose_name_plural = 'Categories'
|
||||||
|
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
# ordering field
|
# ordering field
|
||||||
category_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
category_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
||||||
|
|
||||||
class Project(SortableMixin):
|
class Project(SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['project_order']
|
ordering = ['project_order']
|
||||||
|
|
||||||
category = SortableForeignKey(Category)
|
category = SortableForeignKey(Category)
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
# ordering field
|
# ordering field
|
||||||
project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
# admin.py
|
# admin.py
|
||||||
from adminsortable.admin import SortableAdmin
|
from adminsortable.admin import SortableAdmin
|
||||||
|
|
||||||
from your_app.models import Category, Project
|
from your_app.models import Category, Project
|
||||||
|
|
||||||
admin.site.register(Category, SortableAdmin)
|
admin.site.register(Category, SortableAdmin)
|
||||||
admin.site.register(Project, SortableAdmin)
|
admin.site.register(Project, SortableAdmin)
|
||||||
|
|
||||||
Sometimes you might have a parent model that is not sortable, but has
|
Sometimes you might have a parent model that is not sortable, but has
|
||||||
child models that are. In that case define your models and admin options
|
child models that are. In that case define your models and admin options
|
||||||
|
|
@ -195,42 +196,42 @@ as such:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from adminsortable.fields import SortableForeignKey
|
from adminsortable.fields import SortableForeignKey
|
||||||
|
|
||||||
# models.py
|
# models.py
|
||||||
class Category(models.Model):
|
class Category(models.Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name_plural = 'Categories'
|
verbose_name_plural = 'Categories'
|
||||||
|
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
...
|
...
|
||||||
|
|
||||||
class Project(SortableMixin):
|
class Project(SortableMixin):
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ['project_order']
|
ordering = ['project_order']
|
||||||
|
|
||||||
category = SortableForeignKey(Category)
|
category = SortableForeignKey(Category)
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
# ordering field
|
# ordering field
|
||||||
project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
# admin
|
# admin
|
||||||
from adminsortable.admin import NonSortableParentAdmin, SortableStackedInline
|
from adminsortable.admin import NonSortableParentAdmin, SortableStackedInline
|
||||||
|
|
||||||
from your_app.models import Category, Project
|
from your_app.models import Category, Project
|
||||||
|
|
||||||
class ProjectInline(SortableStackedInline):
|
class ProjectInline(SortableStackedInline):
|
||||||
model = Project
|
model = Project
|
||||||
extra = 1
|
extra = 1
|
||||||
|
|
||||||
class CategoryAdmin(NonSortableParentAdmin):
|
class CategoryAdmin(NonSortableParentAdmin):
|
||||||
inlines = [ProjectInline]
|
inlines = [ProjectInline]
|
||||||
|
|
||||||
admin.site.register(Category, CategoryAdmin)
|
admin.site.register(Category, CategoryAdmin)
|
||||||
|
|
||||||
The ``NonSortableParentAdmin`` class is necessary to wire up the
|
The ``NonSortableParentAdmin`` class is necessary to wire up the
|
||||||
additional URL patterns and JavaScript that Django Admin Sortable needs
|
additional URL patterns and JavaScript that Django Admin Sortable needs
|
||||||
|
|
@ -242,8 +243,8 @@ Backwards Compatibility
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
If you previously used Django Admin Sortable, **DON’T PANIC** -
|
If you previously used Django Admin Sortable, **DON’T PANIC** -
|
||||||
everything will still work exactly as before ***without any changes to
|
everything will still work exactly as before **without any changes to
|
||||||
your code***. Going forward, it is recommended that you use the new
|
your code**. Going forward, it is recommended that you use the new
|
||||||
``SortableMixin`` on your models, as pre-2.0 compatibility might not be
|
``SortableMixin`` on your models, as pre-2.0 compatibility might not be
|
||||||
a permanent thing.
|
a permanent thing.
|
||||||
|
|
||||||
|
|
@ -252,17 +253,17 @@ hard-coded ``order`` field, and meta inheritance requirements:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# legacy model definition
|
# legacy model definition
|
||||||
|
|
||||||
from adminsortable.models import Sortable
|
from adminsortable.models import Sortable
|
||||||
|
|
||||||
class Project(Sortable):
|
class Project(Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
pass
|
pass
|
||||||
title = models.CharField(max_length=50)
|
title = models.CharField(max_length=50)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
Model Instance Methods
|
Model Instance Methods
|
||||||
^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -272,8 +273,8 @@ next or previous instance:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
.get_next()
|
.get_next()
|
||||||
.get_previous()
|
.get_previous()
|
||||||
|
|
||||||
By default, these methods will respect their order in relation to a
|
By default, these methods will respect their order in relation to a
|
||||||
``SortableForeignKey`` field, if present. Meaning, that given the
|
``SortableForeignKey`` field, if present. Meaning, that given the
|
||||||
|
|
@ -281,13 +282,13 @@ following data:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
| Parent Model 1 | |
|
| Parent Model 1 | |
|
||||||
| | Child Model 1 |
|
| | Child Model 1 |
|
||||||
| | Child Model 2 |
|
| | Child Model 2 |
|
||||||
| Parent Model 2 | |
|
| Parent Model 2 | |
|
||||||
| | Child Model 3 |
|
| | Child Model 3 |
|
||||||
| | Child Model 4 |
|
| | Child Model 4 |
|
||||||
| | Child Model 5 |
|
| | Child Model 5 |
|
||||||
|
|
||||||
“Child Model 2” ``get_next()`` would return ``None`` “Child Model 3”
|
“Child Model 2” ``get_next()`` would return ``None`` “Child Model 3”
|
||||||
``get_previous`` would return ``None``
|
``get_previous`` would return ``None``
|
||||||
|
|
@ -297,14 +298,14 @@ If you wish to override this behavior, pass in:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
your_instance.get_next(filter_on_sortable_fk=False)
|
your_instance.get_next(filter_on_sortable_fk=False)
|
||||||
|
|
||||||
You may also pass in additional ORM “extra_filters” as a dictionary,
|
You may also pass in additional ORM “extra_filters” as a dictionary,
|
||||||
should you need to:
|
should you need to:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
your_instance.get_next(extra_filters={'title__icontains': 'blue'})
|
your_instance.get_next(extra_filters={'title__icontains': 'blue'})
|
||||||
|
|
||||||
Adding Sorting to an existing model
|
Adding Sorting to an existing model
|
||||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
@ -322,10 +323,10 @@ Example assuming a model named “Category”:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
for index, category in enumerate(orm.Category.objects.all()):
|
for index, category in enumerate(orm.Category.objects.all()):
|
||||||
category.order = index + 1
|
category.order = index + 1
|
||||||
category.save()
|
category.save()
|
||||||
|
|
||||||
See: `this
|
See: `this
|
||||||
link <http://south.readthedocs.org/en/latest/tutorial/part3.html>`__ for
|
link <http://south.readthedocs.org/en/latest/tutorial/part3.html>`__ for
|
||||||
|
|
@ -348,56 +349,56 @@ To enable sorting in the admin, you need to inherit from
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
from myapp.models import MySortableClass
|
from myapp.models import MySortableClass
|
||||||
from adminsortable.admin import SortableAdmin
|
from adminsortable.admin import SortableAdmin
|
||||||
|
|
||||||
class MySortableAdminClass(SortableAdmin):
|
class MySortableAdminClass(SortableAdmin):
|
||||||
"""Any admin options you need go here"""
|
"""Any admin options you need go here"""
|
||||||
|
|
||||||
admin.site.register(MySortableClass, MySortableAdminClass)
|
admin.site.register(MySortableClass, MySortableAdminClass)
|
||||||
|
|
||||||
To enable sorting on TabularInline models, you need to inherit from
|
To enable sorting on TabularInline models, you need to inherit from
|
||||||
SortableTabularInline:
|
SortableTabularInline:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from adminsortable.admin import SortableTabularInline
|
from adminsortable.admin import SortableTabularInline
|
||||||
|
|
||||||
class MySortableTabularInline(SortableTabularInline):
|
class MySortableTabularInline(SortableTabularInline):
|
||||||
"""Your inline options go here"""
|
"""Your inline options go here"""
|
||||||
|
|
||||||
To enable sorting on StackedInline models, you need to inherit from
|
To enable sorting on StackedInline models, you need to inherit from
|
||||||
SortableStackedInline:
|
SortableStackedInline:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from adminsortable.admin import SortableStackedInline
|
from adminsortable.admin import SortableStackedInline
|
||||||
|
|
||||||
class MySortableStackedInline(SortableStackedInline):
|
class MySortableStackedInline(SortableStackedInline):
|
||||||
"""Your inline options go here"""
|
"""Your inline options go here"""
|
||||||
|
|
||||||
There are also generic equivalents that you can inherit from:
|
There are also generic equivalents that you can inherit from:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from adminsortable.admin import (SortableGenericTabularInline,
|
from adminsortable.admin import (SortableGenericTabularInline,
|
||||||
SortableGenericStackedInline)
|
SortableGenericStackedInline)
|
||||||
"""Your generic inline options go here"""
|
"""Your generic inline options go here"""
|
||||||
|
|
||||||
If your parent model is *not* sortable, but has child inlines that are,
|
If your parent model is *not* sortable, but has child inlines that are,
|
||||||
your parent model needs to inherit from ``NonSortableParentAdmin``:
|
your parent model needs to inherit from ``NonSortableParentAdmin``:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
from adminsortable.admin import (NonSortableParentAdmin,
|
from adminsortable.admin import (NonSortableParentAdmin,
|
||||||
SortableTabularInline)
|
SortableTabularInline)
|
||||||
|
|
||||||
class ChildTabularInline(SortableTabularInline):
|
class ChildTabularInline(SortableTabularInline):
|
||||||
model = YourModel
|
model = YourModel
|
||||||
|
|
||||||
class ParentAdmin(NonSortableParentAdmin):
|
class ParentAdmin(NonSortableParentAdmin):
|
||||||
inlines = [ChildTabularInline]
|
inlines = [ChildTabularInline]
|
||||||
|
|
||||||
Overriding ``queryset()``
|
Overriding ``queryset()``
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -420,25 +421,25 @@ properly determine the sortability of your model. Example:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# add this import to your admin.py
|
# add this import to your admin.py
|
||||||
from adminsortable.utils import get_is_sortable
|
from adminsortable.utils import get_is_sortable
|
||||||
|
|
||||||
|
|
||||||
class ComponentInline(SortableStackedInline):
|
class ComponentInline(SortableStackedInline):
|
||||||
model = Component
|
model = Component
|
||||||
|
|
||||||
def queryset(self, request):
|
def queryset(self, request):
|
||||||
qs = super(ComponentInline, self).queryset(request).filter(
|
qs = super(ComponentInline, self).queryset(request).filter(
|
||||||
title__icontains='foo')
|
title__icontains='foo')
|
||||||
|
|
||||||
# You'll need to add these lines to determine if your model
|
# You'll need to add these lines to determine if your model
|
||||||
# is sortable once we hit the change_form() for the parent model.
|
# is sortable once we hit the change_form() for the parent model.
|
||||||
|
|
||||||
if get_is_sortable(qs):
|
if get_is_sortable(qs):
|
||||||
self.model.is_sortable = True
|
self.model.is_sortable = True
|
||||||
else:
|
else:
|
||||||
self.model.is_sortable = False
|
self.model.is_sortable = False
|
||||||
return qs
|
return qs
|
||||||
|
|
||||||
If you override the queryset of an inline, the number of objects present
|
If you override the queryset of an inline, the number of objects present
|
||||||
may change, and adminsortable won’t be able to automatically determine
|
may change, and adminsortable won’t be able to automatically determine
|
||||||
|
|
@ -480,21 +481,21 @@ independently.
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
class Person(Sortable):
|
class Person(Sortable):
|
||||||
class Meta(Sortable.Meta):
|
class Meta(Sortable.Meta):
|
||||||
verbose_name_plural = 'People'
|
verbose_name_plural = 'People'
|
||||||
|
|
||||||
first_name = models.CharField(max_length=50)
|
first_name = models.CharField(max_length=50)
|
||||||
last_name = models.CharField(max_length=50)
|
last_name = models.CharField(max_length=50)
|
||||||
is_board_member = models.BooleanField('Board Member', default=False)
|
is_board_member = models.BooleanField('Board Member', default=False)
|
||||||
|
|
||||||
sorting_filters = (
|
sorting_filters = (
|
||||||
('Board Members', {'is_board_member': True}),
|
('Board Members', {'is_board_member': True}),
|
||||||
('Non-Board Members', {'is_board_member': False}),
|
('Non-Board Members', {'is_board_member': False}),
|
||||||
)
|
)
|
||||||
|
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return '{} {}'.format(self.first_name, self.last_name)
|
return '{} {}'.format(self.first_name, self.last_name)
|
||||||
|
|
||||||
Extending custom templates
|
Extending custom templates
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -508,15 +509,15 @@ SortableAdmin has two attributes you can override for this use case:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
change_form_template_extends
|
change_form_template_extends
|
||||||
change_list_template_extends
|
change_list_template_extends
|
||||||
|
|
||||||
These attributes have default values of:
|
These attributes have default values of:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
change_form_template_extends = 'admin/change_form.html'
|
change_form_template_extends = 'admin/change_form.html'
|
||||||
change_list_template_extends = 'admin/change_list.html'
|
change_list_template_extends = 'admin/change_list.html'
|
||||||
|
|
||||||
If you need to extend the inline change form templates, you’ll need to
|
If you need to extend the inline change form templates, you’ll need to
|
||||||
select the right one, depending on your version of Django. For 1.10.x or
|
select the right one, depending on your version of Django. For 1.10.x or
|
||||||
|
|
@ -524,15 +525,15 @@ below, you’ll need to extend one of the following:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
templates/adminsortable/edit_inline/stacked-1.10.x.html
|
templates/adminsortable/edit_inline/stacked-1.10.x.html
|
||||||
templates/adminsortable/edit_inline/tabular-inline-1.10.x.html
|
templates/adminsortable/edit_inline/tabular-inline-1.10.x.html
|
||||||
|
|
||||||
otherwise, extend:
|
otherwise, extend:
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
templates/adminsortable/edit_inline/stacked.html
|
templates/adminsortable/edit_inline/stacked.html
|
||||||
templates/adminsortable/edit_inline/tabular.html
|
templates/adminsortable/edit_inline/tabular.html
|
||||||
|
|
||||||
A Special Note About Stacked Inlines…
|
A Special Note About Stacked Inlines…
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
@ -542,6 +543,77 @@ make them difficult to sort. If you anticipate the height of a stacked
|
||||||
inline is going to be very tall, I would suggest using
|
inline is going to be very tall, I would suggest using
|
||||||
SortableTabularInline instead.
|
SortableTabularInline instead.
|
||||||
|
|
||||||
|
Custom JS callbacks after sorting is complete
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
If you need to define a custom event or other callback to be executed
|
||||||
|
after sorting is completed, you’ll need to:
|
||||||
|
|
||||||
|
1. Create a custom template for to add your JavaScript
|
||||||
|
2. Populate the ``after_sorting_js_callback_name`` on your model admin
|
||||||
|
|
||||||
|
An example of this can be found in the “samples” application in the
|
||||||
|
source. Here’s a model admin for a model called “Project”:
|
||||||
|
|
||||||
|
.. code:: python
|
||||||
|
|
||||||
|
class ProjectAdmin(SortableAdmin):
|
||||||
|
inlines = [
|
||||||
|
CreditInline, NoteInline, GenericNoteInline,
|
||||||
|
NonSortableCreditInline, NonSortableNoteInline
|
||||||
|
]
|
||||||
|
list_display = ['__str__', 'category']
|
||||||
|
|
||||||
|
after_sorting_js_callback_name = 'afterSortCallback' # do not include () - just function name
|
||||||
|
sortable_change_list_template = 'adminsortable/custom_change_list.html'
|
||||||
|
sortable_change_form_template = "adminsortable/custom_change_form.html"
|
||||||
|
|
||||||
|
This example is going to add a custom callback on the parent model, and
|
||||||
|
it’s inlines. Here is the JavaScript added to the custom change list:
|
||||||
|
|
||||||
|
.. code:: html+django
|
||||||
|
|
||||||
|
{% extends 'adminsortable/change_list.html' %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
django.jQuery(document).on('order:changed', function(event) {
|
||||||
|
console.log(event.message);
|
||||||
|
// your code here
|
||||||
|
});
|
||||||
|
|
||||||
|
window['{{ after_sorting_js_callback_name }}'] = function() {
|
||||||
|
django.jQuery(document).trigger({ type: 'order:changed', message: 'Order changed', time: new Date() });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
and the custom change form, for the inline models:
|
||||||
|
|
||||||
|
.. code:: html+django
|
||||||
|
|
||||||
|
{% extends "adminsortable/change_form.html" %}
|
||||||
|
|
||||||
|
{% block extrahead %}
|
||||||
|
{{ block.super }}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
django.jQuery(document).on('order:changed', function(event) {
|
||||||
|
console.log(event.message);
|
||||||
|
// your code here
|
||||||
|
});
|
||||||
|
|
||||||
|
window['{{ after_sorting_js_callback_name }}'] = function() {
|
||||||
|
django.jQuery(document).trigger({ type: 'order:changed', message: 'Order changed', time: new Date() });
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
Ideally, you’d pull in a shared piece of code for your callback to keep
|
||||||
|
your code DRY.
|
||||||
|
|
||||||
Django-CMS integration
|
Django-CMS integration
|
||||||
~~~~~~~~~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
|
@ -552,43 +624,43 @@ class allows a change form template to be specified:
|
||||||
|
|
||||||
.. code:: python
|
.. code:: python
|
||||||
|
|
||||||
# example plugin
|
# example plugin
|
||||||
from cms.plugin_base import CMSPluginBase
|
from cms.plugin_base import CMSPluginBase
|
||||||
|
|
||||||
class CMSCarouselPlugin(CMSPluginBase):
|
class CMSCarouselPlugin(CMSPluginBase):
|
||||||
admin_preview = False
|
admin_preview = False
|
||||||
change_form_template = 'cms/sortable-stacked-inline-change-form.html'
|
change_form_template = 'cms/sortable-stacked-inline-change-form.html'
|
||||||
inlines = [SlideInline]
|
inlines = [SlideInline]
|
||||||
model = Carousel
|
model = Carousel
|
||||||
name = _('Carousel')
|
name = _('Carousel')
|
||||||
render_template = 'carousels/carousel.html'
|
render_template = 'carousels/carousel.html'
|
||||||
|
|
||||||
def render(self, context, instance, placeholder):
|
def render(self, context, instance, placeholder):
|
||||||
context.update({
|
context.update({
|
||||||
'carousel': instance,
|
'carousel': instance,
|
||||||
'placeholder': placeholder
|
'placeholder': placeholder
|
||||||
})
|
})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
plugin_pool.register_plugin(CMSCarouselPlugin)
|
plugin_pool.register_plugin(CMSCarouselPlugin)
|
||||||
|
|
||||||
The contents of ``sortable-stacked-inline-change-form.html`` at a
|
The contents of ``sortable-stacked-inline-change-form.html`` at a
|
||||||
minimum need to extend the extrahead block with:
|
minimum need to extend the extrahead block with:
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
{% extends "admin/cms/page/plugin_change_form.html" %}
|
{% extends "admin/cms/page/plugin_change_form.html" %}
|
||||||
{% load static from staticfiles %}
|
{% load static from staticfiles %}
|
||||||
|
|
||||||
{% block extrahead %}
|
{% block extrahead %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
<script src="{% static 'adminsortable/js/jquery-ui-django-admin.min.js' %}"></script>
|
<script src="{% static 'adminsortable/js/jquery-ui-django-admin.min.js' %}"></script>
|
||||||
<script src="{% static 'adminsortable/js/jquery.ui.touch-punch.min.js' %}"></script>
|
<script src="{% static 'adminsortable/js/jquery.ui.touch-punch.min.js' %}"></script>
|
||||||
<script src="{% static 'adminsortable/js/jquery.django-csrf.js' %}"></script>
|
<script src="{% static 'adminsortable/js/jquery.django-csrf.js' %}"></script>
|
||||||
<script src="{% static 'adminsortable/js/admin.sortable.stacked.inlines.js' %}"></script>
|
<script src="{% static 'adminsortable/js/admin.sortable.stacked.inlines.js' %}"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" type="text/css" href="{% static 'adminsortable/css/admin.sortable.inline.css' %}" />
|
<link rel="stylesheet" type="text/css" href="{% static 'adminsortable/css/admin.sortable.inline.css' %}" />
|
||||||
{% endblock extrahead %}
|
{% endblock extrahead %}
|
||||||
|
|
||||||
Sorting within Django-CMS is really only feasible for inline models of a
|
Sorting within Django-CMS is really only feasible for inline models of a
|
||||||
plugin as Django-CMS already includes sorting for plugin instances. For
|
plugin as Django-CMS already includes sorting for plugin instances. For
|
||||||
|
|
@ -596,13 +668,13 @@ tabular inlines, just substitute:
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
<script src="{% static 'adminsortable/js/admin.sortable.stacked.inlines.js' %}"></script>
|
<script src="{% static 'adminsortable/js/admin.sortable.stacked.inlines.js' %}"></script>
|
||||||
|
|
||||||
with:
|
with:
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
<script src="{% static 'adminsortable/js/admin.sortable.tabular.inlines.js' %}"></script>
|
<script src="{% static 'adminsortable/js/admin.sortable.tabular.inlines.js' %}"></script>
|
||||||
|
|
||||||
Notes
|
Notes
|
||||||
~~~~~
|
~~~~~
|
||||||
|
|
@ -612,13 +684,13 @@ Replace the follwing line:
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
{% extends "admin/cms/page/plugin_change_form.html" %}
|
{% extends "admin/cms/page/plugin_change_form.html" %}
|
||||||
|
|
||||||
with
|
with
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
{% extends "admin/cms/page/plugin/change_form.html" %}
|
{% extends "admin/cms/page/plugin/change_form.html" %}
|
||||||
|
|
||||||
From ``django-admin-sortable 2.0.13`` the ``jquery.django-csrf.js`` was
|
From ``django-admin-sortable 2.0.13`` the ``jquery.django-csrf.js`` was
|
||||||
removed and you have to include the snippet-template. Change the
|
removed and you have to include the snippet-template. Change the
|
||||||
|
|
@ -626,13 +698,13 @@ following line:
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
<script type="text/javascript" src="{% static 'adminsortable/js/jquery.django-csrf.js' %}"></script>
|
<script type="text/javascript" src="{% static 'adminsortable/js/jquery.django-csrf.js' %}"></script>
|
||||||
|
|
||||||
to
|
to
|
||||||
|
|
||||||
.. code:: html+django
|
.. code:: html+django
|
||||||
|
|
||||||
{% include 'adminsortable/csrf/jquery.django-csrf.html' with csrf_cookie_name='csrftoken' %}
|
{% include 'adminsortable/csrf/jquery.django-csrf.html' with csrf_cookie_name='csrftoken' %}
|
||||||
|
|
||||||
Please note, if you change the ``CSRF_COOKIE_NAME`` you have to adjust
|
Please note, if you change the ``CSRF_COOKIE_NAME`` you have to adjust
|
||||||
``csrf_cookie_name='YOUR_CSRF_COOKIE_NAME'``
|
``csrf_cookie_name='YOUR_CSRF_COOKIE_NAME'``
|
||||||
|
|
@ -674,10 +746,3 @@ License
|
||||||
~~~~~~~
|
~~~~~~~
|
||||||
|
|
||||||
django-admin-sortable is released under the Apache Public License v2.
|
django-admin-sortable is released under the Apache Public License v2.
|
||||||
|
|
||||||
.. |PyPI version| image:: https://img.shields.io/pypi/v/django-admin-sortable.svg
|
|
||||||
:target: https://pypi.python.org/pypi/django-admin-sortable
|
|
||||||
.. |Python versions| image:: https://img.shields.io/pypi/pyversions/django-admin-sortable.svg
|
|
||||||
:target: https://pypi.python.org/pypi/django-admin-sortable
|
|
||||||
.. |Build Status| image:: https://travis-ci.org/alsoicode/django-admin-sortable.svg?branch=master
|
|
||||||
:target: https://travis-ci.org/alsoicode/django-admin-sortable
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
VERSION = (2, 1, 5)
|
VERSION = (2, 1, 6)
|
||||||
DEV_N = None
|
DEV_N = None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue