跳转到主要内容

Django admin中的模型和内联模型的拖放排序。

项目描述

PyPI version Python versions Build Status

该项目使您能够轻松地将拖放排序添加到Django admin中的任何模型。可排序模型的内联也可以设置为可排序,从而允许单个项或项组可排序。

如果您发现Django Admin Sortable很有帮助,请考虑 为我买杯咖啡

具有可排序父级的排序模型实例

sortable-models

sortable-models

内联排序

sortable-inlines

sortable-inlines

支持的Django版本

对于Django 4,使用最新版本

对于Django 3,使用2.2.4

对于Django 1.8.x < 3.0,使用2.1.8。

对于Django 1.5.x到1.7.x,使用版本2.0.18。

有关版本的其他注意事项

django-admin-sortable 1.5.2为Django 1.4.x引入了不兼容的更改

django-admin-sortable 1.6.6为sorting_filters属性引入了不兼容的更改。如果您还没有,请将您的属性转换为新的基于元组的格式。

django-admin-sortable 1.7.1及以上版本兼容Python 3。

django-admin-sortable 2.1.6 存在漏洞。请不要使用它 :)

安装

  1. $ pip install django-admin-sortable

–or–

源代码下载 django-admin-sortable

  1. 解压缩目录,并进入解压后的项目目录

    • 可选:启用你的虚拟环境

  2. 运行 $ python setup.py install 或将 adminsortable 添加到你的 PYTHONPATH。

配置

  1. adminsortable 添加到你的 INSTALLED_APPS

  2. 确保 django.template.context_processors.static 在你的 TEMPLATES["OPTIONS"]["context_processors"]

    • (在 Django 的旧版本中,确保 django.core.context_processors.staticTEMPLATE_CONTEXT_PROCESSORS 中。)

  3. 确保 CSRF_COOKIE_HTTPONLY 没有设置为 True,因为 django-admin-sortable 目前与此设置不兼容。

静态媒体

推荐:使用staticfiles 应用

备用:将 adminsortable 文件夹从 static 文件夹复制到您提供静态文件的服务位置。

测试

查看包含的 sample_project 以了解工作示例。管理员登录凭证为:admin/admin

当一个模型可排序时,会添加一个工具区域链接,显示“更改顺序”。点击此链接,您将被带到自定义视图,您可以在此处拖放记录以排序。

内联可以直接从更改表单拖放到任何顺序。

使用方法

模型

要将“可排序性”添加到模型,您需要继承 SortableMixin 并至少定义

  • 用于 Meta.ordering 的字段,该字段必须解析为 Django ORM 中定义的一个整数字段

  • PositiveIntegerField

  • IntegerField

  • PositiveSmallIntegerField

  • SmallIntegerField

  • BigIntegerField

  • Meta.ordering 只能包含一个值,否则,您的对象将无法正确排序。

  • 重要:您必须将用于排序的字段命名为除“order_field”之外的其他名称,因为此名称已被 SortableMixin 类保留。

  • 建议您将 Meta.ordering 中定义的字段设置为 editable=Falsedb_index=True,以获得无缝的 Django 管理员体验并加快对象的查找速度。

示例模型

# models.py
from adminsortable.models import SortableMixin

class MySortableClass(SortableMixin):
    title = models.CharField(max_length=50)

    class Meta:
        verbose_name = 'My Sortable Class'
        verbose_name_plural = 'My Sortable Classes'
        ordering = ['the_order']


    # define the field the model should be ordered by
    the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)

    def __unicode__(self):
        return self.title

版本 2.0.20 或更高版本也支持不使用 AutoField 的主键的模型。

常见用例

一个常见用例是拥有相对于父对象可排序的子对象。如果您的父对象也可排序,以下是您设置模型和管理员选项的方法

# models.py
from adminsortable.fields import SortableForeignKey

class Category(SortableMixin):
    class Meta:
        ordering = ['category_order']
        verbose_name_plural = 'Categories'

    title = models.CharField(max_length=50)

    # ordering field
    category_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)

class Project(SortableMixin):
    class Meta:
        ordering = ['project_order']

    category = SortableForeignKey(Category)
    title = models.CharField(max_length=50)

    # ordering field
    project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)

    def __unicode__(self):
        return self.title

# admin.py
from adminsortable.admin import SortableAdmin

from your_app.models import Category, Project

admin.site.register(Category, SortableAdmin)
admin.site.register(Project, SortableAdmin)

有时您可能有一个不可排序的父模型,但具有可排序的子模型。在这种情况下,定义您的模型和管理员选项如下

from adminsortable.fields import SortableForeignKey

# models.py
class Category(models.Model):
    class Meta:
        verbose_name_plural = 'Categories'

    title = models.CharField(max_length=50)
    ...

class Project(SortableMixin):
    class Meta:
        ordering = ['project_order']

    category = SortableForeignKey(Category)
    title = models.CharField(max_length=50)

    # ordering field
    project_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)

    def __unicode__(self):
        return self.title

# admin
from adminsortable.admin import NonSortableParentAdmin, SortableStackedInline

from your_app.models import Category, Project

class ProjectInline(SortableStackedInline):
    model = Project
    extra = 1

class CategoryAdmin(NonSortableParentAdmin):
    inlines = [ProjectInline]

admin.site.register(Category, CategoryAdmin)

NonSortableParentAdmin 类是必要的,用于连接 Django Admin Sortable 需要的额外 URL 模式和 JavaScript,以便使您的模型可排序。子模型不需要是内联模型,它可以直接连接到 Django 管理员,并且在排序时将根据不可排序的外键对对象进行分组。

向后兼容性

如果您之前使用过 Django Admin Sortable,不要慌张 - 仍然会像以前一样完全正常工作 无需对您的代码进行任何更改。向前看,建议您在模型上使用新的 SortableMixin,因为 2.0 之前的兼容性可能不是永久的。

请注意,Sortable类仍然包含硬编码的order字段和元继承需求。

# legacy model definition

from adminsortable.models import Sortable

class Project(Sortable):
    class Meta(Sortable.Meta):
        pass
    title = models.CharField(max_length=50)

    def __unicode__(self):
        return self.title

模型实例方法

可排序模型的每个实例都有两个便捷方法来获取下一个或前一个实例

.get_next()
.get_previous()

默认情况下,这些方法将尊重其相对于SortableForeignKey字段(如果存在)的顺序。这意味着,给定以下数据

| Parent Model 1 |               |
|                | Child Model 1 |
|                | Child Model 2 |
| Parent Model 2 |               |
|                | Child Model 3 |
|                | Child Model 4 |
|                | Child Model 5 |

“子模型2” get_next()将返回None,“子模型3” get_previous将返回None

如果您希望覆盖此行为,请传入:filter_on_sortable_fk=False

your_instance.get_next(filter_on_sortable_fk=False)

如果您需要,也可以传入额外的ORM“filer_args”列表或“filter_kwargs”字典

your_instance.get_next(
    filter_args=[Q(field1=True) | Q(field2=True)],
    filter_kwargs={'title__icontains': 'blue'}
)

弃用警告

以前“filter_kwargs”被命名为“extra_filters”。随着“filter_args”的添加,“extra_filters”被重命名为保持一致性。

向现有模型添加排序

Django 1.5.x到1.6.x

如果您正在向现有模型添加排序,建议您使用django-south来创建一个模式迁移,以将“order”字段添加到您的模型中。您还需要创建一个数据迁移,以便为“order”列添加适当的值。

示例假设有一个名为“Category”的模型

def forwards(self, orm):
    for index, category in enumerate(orm.Category.objects.all()):
        category.order = index + 1
        category.save()

更多信息请参阅:此链接

Django 1.7.x或更高版本

由于模式迁移已集成到Django 1.7中,因此您不需要使用south,但添加和运行迁移的过程几乎相同。请参阅迁移文档以开始。

Django管理集成

要启用管理中的排序,您需要从SortableAdmin继承

from django.contrib import admin
from myapp.models import MySortableClass
from adminsortable.admin import SortableAdmin

class MySortableAdminClass(SortableAdmin):
    """Any admin options you need go here"""

admin.site.register(MySortableClass, MySortableAdminClass)

要启用TabularInline模型上的排序,您需要从SortableTabularInline继承

from adminsortable.admin import SortableTabularInline

class MySortableTabularInline(SortableTabularInline):
    """Your inline options go here"""

要启用StackedInline模型上的排序,您需要从SortableStackedInline继承

from adminsortable.admin import SortableStackedInline

class MySortableStackedInline(SortableStackedInline):
   """Your inline options go here"""

还有您可以从其继承的通用等效项

from adminsortable.admin import (SortableGenericTabularInline,
    SortableGenericStackedInline)
    """Your generic inline options go here"""

如果您的父模型不是可排序的,但具有可排序的子内联,则父模型需要从NonSortableParentAdmin继承

from adminsortable.admin import (NonSortableParentAdmin,
    SortableTabularInline)

class ChildTabularInline(SortableTabularInline):
    model = YourModel

class ParentAdmin(NonSortableParentAdmin):
    inlines = [ChildTabularInline]

重写queryset()

django-admin-sortable支持在Django管理中的管理模型和内联模型上的自定义queryset重写!

如果您提供了对SortableAdmin或Sortable内联模型的覆盖,则无需执行任何额外操作。django-admin-sortable将自动尊重您的queryset。

请查看示例项目中WidgetAdmin类的示例,该示例展示了具有自定义queryset()覆盖的管理类。

为内联模型重写queryset()

这是一个特殊情况,需要几行额外的代码才能正确确定模型的排序性。示例

# add this import to your admin.py
from adminsortable.utils import get_is_sortable


class ComponentInline(SortableStackedInline):
    model = Component

    def queryset(self, request):
        qs = super(ComponentInline, self).queryset(request).filter(
            title__icontains='foo')

        # You'll need to add these lines to determine if your model
        # is sortable once we hit the change_form() for the parent model.

        if get_is_sortable(qs):
            self.model.is_sortable = True
        else:
            self.model.is_sortable = False
        return qs

如果您覆盖了内联的queryset,对象的数量可能会改变,而adminsortable无法自动确定内联模型是否可排序,这就是为什么我们必须在此方法中设置模型的is_sortable属性。

排序对象子集

还可以通过添加sorting_filters元组来对模型中的对象子集进行排序。这与QuerySet上的.filter()完全相同,并在get_queryset()上应用在管理类之后,允许您像在管理中通常那样重写queryset,但应用额外的排序过滤器。文本“更改顺序”将显示在更改列表模板中每个过滤器的开头,并且过滤组按列表顺序从左到右显示。如果没有指定sorting_filters,则链接将显示“更改顺序”文本。

自引用SortableForeignKey

您可以指定一个自引用的可排序外键字段,但是当前管理界面会显示一个与子字段处于同一级别的孙子模型。我正在努力解决这个问题。

重要!

django-admin-sortable 1.6.6 版本对 sorting_filters 引入了一个向后不兼容的更改。之前这个属性被定义为字典,所以您需要将值更改为新的基于元组的格式。

排序子集的一个例子是“董事会”。在这个用例中,您有一个“人员”对象的列表。其中一些人属于董事会,而另一些人则不属于,您需要独立地对他们进行排序。

class Person(Sortable):
    class Meta(Sortable.Meta):
        verbose_name_plural = 'People'

    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    is_board_member = models.BooleanField('Board Member', default=False)

    sorting_filters = (
        ('Board Members', {'is_board_member': True}),
        ('Non-Board Members', {'is_board_member': False}),
    )

    def __unicode__(self):
        return '{} {}'.format(self.first_name, self.last_name)

扩展自定义模板

默认情况下,adminsortable的更改表和更改列表视图继承自Django管理标准的模板。有时您需要自定义更改表或更改列表,但同时还需要adminsortable的CSS和JavaScript,以便对可排序的行内模型进行排序。

SortableAdmin有两个属性可以覆盖此用例

change_form_template_extends
change_list_template_extends

这些属性的默认值如下

change_form_template_extends = 'admin/change_form.html'
change_list_template_extends = 'admin/change_list.html'

如果您需要扩展行内更改表模板,您需要根据您的Django版本选择正确的模板。对于1.10.x或以下版本,您需要扩展以下之一

templates/adminsortable/edit_inline/stacked-1.10.x.html
templates/adminsortable/edit_inline/tabular-inline-1.10.x.html

否则,扩展

templates/adminsortable/edit_inline/stacked.html
templates/adminsortable/edit_inline/tabular.html

关于堆叠行内的一些特别说明…

堆叠行内模型的高度可以动态增加,这可能会使它们难以排序。如果您预计堆叠行内的高度会非常高,我建议使用SortabelTabularInline。

排序完成后执行自定义JS回调

如果您需要在排序完成后定义自定义事件或其他回调,您需要

  1. 创建一个自定义模板以添加您的JavaScript

  2. 在您的模型管理上填写 after_sorting_js_callback_name

以下是一个示例,可以在源代码的“samples”应用程序中找到。这是一个名为“Project”的模型管理器

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"

以下示例将在父模型及其行内上添加自定义回调,这里是添加到自定义更改列表的自定义JavaScript

{% 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 %}

以及行内模型的自定义更改表

{% 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 %}

理想情况下,您将提取回调的共享代码,以保持您的代码简洁。

Django-CMS集成

Django-CMS插件使用它们自己的更改表,因此不会自动包含django-admin-sortable工作所需的JavaScript。幸运的是,这很容易解决,因为CMSPlugin类允许指定更改表模板。

# example plugin
from cms.plugin_base import CMSPluginBase

class CMSCarouselPlugin(CMSPluginBase):
    admin_preview = False
    change_form_template = 'cms/sortable-stacked-inline-change-form.html'
    inlines = [SlideInline]
    model = Carousel
    name = _('Carousel')
    render_template = 'carousels/carousel.html'

    def render(self, context, instance, placeholder):
        context.update({
            'carousel': instance,
            'placeholder': placeholder
        })
        return context

plugin_pool.register_plugin(CMSCarouselPlugin)

至少需要在 sortable-stacked-inline-change-form.html 中扩展extrahead块

{% extends "admin/cms/page/plugin_change_form.html" %}
{% load static from staticfiles %}

{% block extrahead %}
    {{ block.super }}
    <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.django-csrf.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' %}" />
{% endblock extrahead %}

Django-CMS中的排序仅适用于插件行内模型,因为Django-CMS已经包含了插件实例的排序。对于表格行内模型,只需将

<script src="{% static 'adminsortable/js/admin.sortable.stacked.inlines.js' %}"></script>

替换为

<script src="{% static 'adminsortable/js/admin.sortable.tabular.inlines.js' %}"></script>

备注

从django-cms 3.x开始,change_form.html的路径已更改。将以下行替换为

{% extends "admin/cms/page/plugin_change_form.html" %}

替换为

{% extends "admin/cms/page/plugin/change_form.html" %}

从django-admin-sortable 2.0.13开始,已删除jquery.django-csrf.js,您必须包含snippet-template。将以下行更改为

<script type="text/javascript" src="{% static 'adminsortable/js/jquery.django-csrf.js' %}"></script>

更改为

{% include 'adminsortable/csrf/jquery.django-csrf.html' with csrf_cookie_name='csrftoken' %}

请注意,如果您更改CSRF_COOKIE_NAME,您必须调整csrf_cookie_name='YOUR_CSRF_COOKIE_NAME'

理由

其他项目已经将拖放排序添加到ChangeList视图,但这引入了一些问题…

  • ChangeList视图支持分页,这使得跨页面的拖放排序变得不可能。

  • 默认情况下,ChangeList视图不会根据外键排序记录,也不会区分与外键关联的行。这使得按外键分组排序记录成为不可能。

  • ChangeList支持行内编辑,在我看来,在此基础上添加拖放排序似乎有些过多。

状态

目前在生产中使用django-admin-sortable。

2.3.0版本的新特性是什么?

  • Django 4兼容性

未来

  • 更好地支持自引用外键的模板。如果有人愿意负责渲染递归排序,那将是极好的。

许可证

django-admin-sortable采用Apache公共许可证v2发布。

项目详情


发布历史 发布通知 | RSS源

下载文件

下载适用于您平台的文件。如果您不确定选择哪个,请了解更多关于安装包的信息。

源分布

django-admin-sortable-2.3.tar.gz (107.0 kB 查看哈希值)

上传时间

构建分布

django_admin_sortable-2.3-py2-none-any.whl (115.1 kB 查看哈希值)

上传时间 Python 2

支持者