DjangoBrothers BLOG ✍️

2018/12/08

このエントリーをはてなブックマークに追加
Django Admin

Django Admin Sortableでモデルインスタンスの順番を手動で並び替えする

Djangoの管理画面上でモデルインスタンスの順番を手動で変更できるようになるDjango Admin Sortableというライブラリの使い方を説明します。

このライブラリを使用することで、通常のモデルインスタンスのように時系列順などのデフォルトの並び順ではなく、手動で設定した順番に簡単に変更することができます。Adminページ上でドラッグ&ドロップで変更できるようになるのでとても便利です。

インストールとsettings.pyへの設定

以下のコマンドでpipにライブラリをインストールできます。(ソースコードからインストールするにはgithubのREADMEを確認してください)

pipにライブラリをインストール

$ pip install django-admin-sortable

次に、settings.pyINSTALLED_APPSadminsortableを追加します。

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'app',
    'adminsortable', # これを追加します
]

この際に、django.template.context_processors.staticTEMPLATES['OPTIONS']['context_processors']に含まれているか確認してください。

基本編(特定のモデルの順番を変更できるようにする)

具体的な例を用いて、基本的な使い方を説明していきます。

今回は、Django Brothersのように、チュートリアル形式のサービスを作っていると仮定します。一つのチュートリアル(Tutorialモデル)には、複数のレッスン(Lessonモデル)が紐づいており、Adminページから管理することができます。チュートリアルはレベル別で表示したり、チュートリアルの中盤にあとからレッスンを追加したいときなど、モデルインスタンスの順番を簡単に管理・変更できる機能があると便利です。そこで、Django Admin Sortableを使用して、この機能を実装していきましょう。

今回のベースとなるモデルの構成は以下のとおりになります。

tutorials/models.py

from django.db import models

class Tutorial(models.Model):
    title = models.CharField(max_length=50)
    description = models.TextField(blank=True, null=True)

    def __str__(self):
        return self.title

class Lesson(models.Model):
    title = models.CharField(max_length=100)
    text = models.TextField(blank=True, null=True)
    tutorial = models.ForeignKey(Tutorial, on_delete=models.CASCADE, related_name="lessons")

    def __str__(self):
        return self.title

Adminページにはこのようなインスタンスがすでに入力されています。

Tutorialモデル

Lessonモデル

それではまずチュートリアルモデルをソートできるようにしてみましょう。特定のモデルインスタンスの順番をソートできるようにするためには、そのモデルにadminsortable.modelsSortableMixinを継承させてあげます。

また、順番を決めるために必要なフィールドを追加する必要があります。このときに利用するフィールドの型はいくつか選ぶことができますが、基本的にはmodels.PositiveIntegerFieldを指定すれば問題ないでしょう。このフィールド名はthe_orderのような名前で良いのですが、order_fieldという名前はすでにこのライブラリ内で使用されているので使わないようにしましょう。

次に、モデルのMeta.orderingに上で設定したthe_orderというフィールドを指定してあげます。最後に、admin.pyでadminsortable.admin.SortableAdminを継承させてあげれば完成です。サンプルコードは以下になります。

Tutorialモデルをソートできるようにする

tutorials/models.py

from django.db import models
from adminsortable.models import SortableMixin # 追加する

# SortableMixinを継承する
class Tutorial(SortableMixin):
    title = models.CharField(max_length=50)
    description = models.TextField(blank=True, null=True)
    the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True) # 追加する

    class Meta:
        ordering = ["the_order"] # 上記で追加したフィールドを指定

    def __str__(self):
        return self.title

tutorials/admin.py

from django.contrib import admin
from .models import Tutorial
from adminsortable.admin import SortableAdmin

# SortableAdminを継承
class TutorialAdmin(SortableAdmin):
    list_display = ('id', 'title')
    list_display_links = ('id', 'title')

admin.site.register(Tutorial, TutorialAdmin)

ここまで設定すると、チュートリアルのAdminページの右上にCHANGE ORDERというボタンが表示され、そちらのページから簡単に順番を変更することができるようになります。

TutorialモデルのCHANGE ORDERページ

応用編(親子関係を持つモデル内でのソート)

より実践的な例として、親子関係を持つモデルでソートできるようにしてみます。引き続き、上記のTutorialモデルとLessonモデルで説明をしていきます。

Tutorialは複数のLessonを持っていますが、最初に作ったカリキュラムに変更があり、一度公開したチュートリアルの中盤に新しいレッスンを追加する必要があります。そんなときには、そのチュートリアル内でレッスンの順番を簡単に変えることができると便利です。

特定の親モデル内でのみソートできるようにするには、django.db.models.ForeignKeyadminsortable.fields.SortableForeignKeyに置き換えることで実現できます。

元の実装から変更を入れてみましょう。以下がサンプルコードです。

tutorials/models.py

from django.db import models
from adminsortable.models import SortableMixin
from adminsortable.fields import SortableForeignKey # 追加する

class Tutorial(SortableMixin):
    title = models.CharField(max_length=50)
    description = models.TextField(blank=True, null=True)
    the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True)

    class Meta:
        ordering = ["the_order"]

    def __str__(self):
        return self.title

class Lesson(SortableMixin):
    title = models.CharField(max_length=100)
    text = models.TextField(blank=True, null=True)
    tutorial = SortableForeignKey(Tutorial, on_delete=models.CASCADE, related_name="lessons") # SortableForeignKeyに変更
    the_order = models.PositiveIntegerField(default=0, editable=False, db_index=True) # 追加する

    class Meta:
        ordering = ["the_order"] # 追加する

    def __str__(self):
        return self.title

tutorials/admin.py

from django.contrib import admin
from .models import Tutorial, Lesson
from adminsortable.admin import SortableAdmin

# SortableAdminを継承
class TutorialAdmin(SortableAdmin):
    list_display = ('id', 'title')
    list_display_links = ('id', 'title')

# SortableAdminを継承
class LessonAdmin(SortableAdmin):
    list_display = ('id', 'title', 'tutorial')
    list_display_links = ('id', 'title')

admin.site.register(Lesson, LessonAdmin)
admin.site.register(Tutorial, TutorialAdmin)

ここまでの実装で、LessonモデルのAdminページにもCHANGE ORDERのボタンが表示されるようになり、このように親子関係を維持したままモデルの順番を簡単に変更することができるようになります。

LessonモデルのCHANGE ORDERページ

まとめ

今回はAdminページでモデルインスタンスの順番を簡単に変更できるようにするベーシックなやり方を紹介しました。Webサービスでは、時系列ではなく手動で表示順をコントロールしたい場面もよくでてきますので、このライブラリの使い方を覚えておくと良いでしょう。他にもいろいろな形式のソートを指定できますので、より詳しく勉強してみたい方はDjango Admin SortableのREADMEを読んでみてください。