こんにちは。KOUKIです。
皆さんは、PythonのフレームワークであるDjangoでの開発経験はありますでしょうか?
Djangoはとても便利なフレームワークで、管理機能が標準搭載されています。
しかも、この管理機能はカスタマイズ性に優れており、様々な機能を追加することができます。
今日は、Djangoの便利な管理機能について、ご紹介したいと思います。
<目次>
事前準備
以下の構成になるようにしてください。
1 2 3 4 5 6 7 8 |
$ tree . ├── Dockerfile ├── Makefile ├── app ├── docker-compose.yml ├── requirements.txt |
また、requirements.txtについては、管理画面作成用に以下の内容でアップデートしてください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
Django>=2.1.3,<2.2.0 djangorestframework>=3.9.0,<3.10.0 flake8>=3.6.0,<3.7.0 psycopg2>=2.7.5,<2.8.0 Pillow>=5.3.0,<5.4.0 pytest-django==3.5.1 django-cors-headers==2.4.0 Faker==1.0.1 django-summernote==0.8.11.4 django-admin-list-filter-dropdown==1.0.2 django-admin-rangefilter==0.5.0 django-leaflet django-geojson [field] django-import-export django-grappelli django-admin-honeypot |
プロジェクトを作成します。
1 |
make django |
続いてアプリケーションを作成します。
1 |
make app app=main |
settgins.pyにmainを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
vi app/app/settings.py INSTALLED_APPS = [ 'main.apps.MainConfig', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
注意事項
今回は、PostgreSQLではなく、SQLite3を使います。そのため、settings.pyのデータベースの設定はいじりません。
管理ユーザーの作成
次のコマンドで、管理ユーザーを作成しましょう。
1 2 3 4 5 6 7 8 9 |
$ make migrate $ make admin Username (leave blank to use 'user'): admin Email address: Password: Password (again): Superuser created successfully. |
dockerコンテナを立ち上げます。
1 |
docker-compose up |
「localhost:8000/admin」にアクセスして、管理画面に入れるか確認しましょう。


管理画面のヘッダーとタイトルを変更
app/urls.pyに次の設定を行うとサイトのヘッダーとタイトルを変更することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
vi app/app/urls.py from django.contrib import admin from django.urls import path # new admin.site.site_header = 'SELF NOTE' # Change title admin.site.site_title = 'SELF NOTE' # Change URL title admin.site.index_title = 'SELF NOTE ADMINISTRATION' # Change title2 urlpatterns = [ path('admin/', admin.site.urls), ] |


モデルを管理画面に登録する
モデルを管理画面に登録してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
vi app/main/models.py from django.db import models class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) is_draft = models.BooleanField(default=True) def __str__(self): return self.title |
マイグレーションをしましょう。
1 |
make migrate |
main/admin.pyにBlogモデルを登録します。
1 2 3 4 5 6 7 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog admin.site.register(Blog) |
管理画面を確認してみましょう。

Blogsが表示されましたね。
Blogのヘッダを変更する
Blogのヘッダも変更できます。
1 2 3 4 5 6 7 8 9 |
vi app/main/apps.py from django.apps import AppConfig class MainConfig(AppConfig): name = 'main' verbose_name = 'Blog Controller' |
画面を確認してみましょう。

「BLOG CONTROLLER」という文字列が表示されましたね。
データ列の変更
Blogデータの一覧に表示されるデータ列を変更してみましょう。
1 2 3 4 5 6 7 8 9 10 11 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') admin.site.register(Blog, BlogAdmin) |
Blogにデータを追加して、画面を表示させてみましょう。

Fakerで大量データを作成する
Fakerという便利な3rdパーティー製のデータ生成モジュールがあります。
簡単な使い方を紹介します。
1 2 3 4 5 6 7 8 9 10 11 12 |
# dockerコンテナの中のshellに入る docker-compose exec app python manage.py shell >>> from main.models import Blog >>> from faker import Faker >>> faker = Faker() >>> faker <faker.generator.Generator object at 0x7ff777f39090> >>> faker.first_name() 'Nancy' >>> faker.first_name() 'Catherine |
fakerオブジェクトを作成し、既定のプロパティでアクセスするとランダムな文字列を返してくれます。
大量のデータを一気に作りたい場合は、有効です。
以下のプログラムを実行して、500件のデータを作成しましょう。
1 2 |
for _ in range(0, 500): Blog.objects.create(title=faker.sentence(), body=faker.paragraph()) |
データが作成し終えたら、dockerコンテナの中のshellから抜けます。
1 |
exit() |
画面をリロードすると500件のデータが投入されているはずです。

フィルターを付けよう
便利なことに、フィルターも付けられます。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft',) # new admin.site.register(Blog, BlogAdmin) |
管理画面を見てみるとfilterが表示されることがわかると思います。

適当なデータの「IS DRAFT」をFalseにしてみましょう。

Filterの「No」を押下するとFalseにしたデータだけが表示されることがわかると思います。

カスタム検索機能の実装
管理画面上に検索機能を実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft',) search_fields = ('title', ) def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') # - は、降順の意味 return ('title',) admin.site.register(Blog, BlogAdmin) |
search_fieldsを付与しました。
管理画面を確認してみましょう。検索窓が表示されるはずです。

prepopulated field
propopulated filed を設定してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
vi app/main/models.py from django.db import models class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) is_draft = models.BooleanField(default=True) slug = models.SlugField(max_length=100) # 追加 def __str__(self): return self.title |
マイグレーションを実行しましょう。今回は、すでに既存のデータが入った状態なので、少し注意が必要です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
make migrate docker-compose run --rm app sh -c "python manage.py makemigrations" Starting django-admin_db_1 ... done You are trying to add a non-nullable field 'slug' to blog without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option: 1 # 1を選択 Please enter the default value now, as valid Python The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now >>> "" # 空を指定 Migrations for 'main': main/migrations/0002_blog_slug.py - Add field slug to blog docker-compose run --rm app sh -c "python manage.py migrate" Starting django-admin_db_1 ... done Operations to perform: Apply all migrations: admin, auth, contenttypes, main, sessions Running migrations: Applying main.0002_blog_slug... OK |
admin.pyも変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft',) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} # new def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) admin.site.register(Blog, BlogAdmin) |
blogのデータを追加してみましょう。
Titleの追加と同時に、slugフィールドが入力されたことがわかると思います。

ページネーションの実装
ページネーションも簡単に実装できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft', 'date_created') # new list_per_page = 50 # new search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) admin.site.register(Blog, BlogAdmin) |
画面を表示してみましょう。

アクションの追加
管理画面のアクションを追加することができます。
例えば、is_draftをFalseからTrueに変更するアクションを追加できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) # new search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) # new def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
管理画面から確認してみましょう。

各データのチェックボックスをチェックした状態で、このアクションを実行するとIS_DRAFTがfalseに変わります。
データヒエラルキーの追加
データヒエラルキーを追加できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' # new search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
管理画面を確認してみましょう。

画面項目の変更
fieldsを利用すると項目の順番を変更できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fields = (('title', 'slug'), 'body', 'is_draft', ) # new search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
管理画面を確認してみましょう。

より詳細な変更には、fieldsetsを利用します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
vi app/main/admin.py from django.contrib import admin from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft') list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' # new fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
管理画面を確認しましょう。

モデルのプロパティ追加
管理画面上にモデルのプロパティの値を表示できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from main.models import Blog class BlogAdmin(admin.ModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
管理画面を確認しましょう。

リッチテキストエディター
django-summoernoteで、リッチテキストエディターを追加してみましょう。
settings.pyにこのモジュールを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR, 'media/') |
画像を追加できるようにMEDIA_URL, MEDIA_ROOTもついでに設定しておきました。
urls.pyも変更する必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/app/urls.py from django.contrib import admin from django.conf import settings from django.conf.urls.static import static # new from django.urls import include, path # new admin.site.site_header = 'SELF NOTE' # Change title admin.site.site_title = 'SELF NOTE' # Change URL title admin.site.index_title = 'SELF NOTE ADMINISTRATION' # Change title2 urlpatterns = [ path('admin/', admin.site.urls), path('summernote/', include('django_summernote.urls')), ] # https://docs.djangoproject.com/en/1.11/topics/files/ if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
最後にadmin.pyを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin # new from main.models import Blog class BlogAdmin(SummernoteModelAdmin): # change list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) # new def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) |
画面を確認してみましょう。

コメントモデルを作成
コメントモデルを作成しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
vi app/main/models.py from django.db import models class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) is_draft = models.BooleanField(default=True) slug = models.SlugField(max_length=100) def __str__(self): return self.title # new class Comment(models.Model): blog = models.ForeignKey(Blog, related_name='comments', on_delete=models.CASCADE) text = models.TextField() is_active = models.BooleanField(default=True) date_created = models.DateTimeField(auto_now_add=True) def __str__(self): return self.text |
マイグレーションを実施しましょう。
1 |
make migrate |
管理画面からCommentモデルが確認できるようにadmin.pyに追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment) |
管理画面を確認してみましょう。
CommentもFakerを使って、ダミーデータを作成しておきましょう。
1 2 3 4 5 6 7 8 9 |
docker-compose exec app python manage.py shell >>> from main.models import Blog, Comment >>> from faker import Faker >>> faker = Faker() >>> for blog in Blog.objects.iterator(): comments = [Comment(text=faker.paragraph()) for _ in range(0, 3)] comments = [Comment(text=faker.paragraph(), blog=blog) for _ in range(0, 3)] Comment.objects.bulk_create(comments) |
これで、Blogに紐づくCommentを作成できます。
インライン処理
Blogの投稿画面の中に、Commentも表示できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment # new class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) # new def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment) |
管理画面を確認してみましょう。


データ一覧上でデータを編集
コメントデータの一覧上でデータを編集できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 # new class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) # edit |
管理画面を確認してみましょう。

コメントの表示・非表示(collapse)
Blogの投稿画面上のコメントに表示・非表示機能を付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
vi app/main/admin.py from django.contrib import admin from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) # new class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), # new }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) |

クエリーセットのオーバーライド
デフォルトのクエリーセットをオーバーライドして、任意の処理にすることができます。
例えば、ブログに紐づけられたコメント数を表示させたい場合は、次のようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count # new from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) # new list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) # new def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset # new def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) |
get_queryset関数で、デフォルトのクエリーセットをオーバーライドし、no_of_comments関数でコメントをカウントする処理を追加しました。
それを、Blogのlist_displayに追加してあげれば、画面上に表示されます。
管理画面を確認してみましょう。

カテゴリの追加
カテゴリを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
vi app/main/models.py from django.db import models class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) is_draft = models.BooleanField(default=True) slug = models.SlugField(max_length=100) categories = models.ManyToManyField('main.Category') # new def __str__(self): return self.title class Comment(models.Model): blog = models.ForeignKey(Blog, related_name='comments', on_delete=models.CASCADE) text = models.TextField() is_active = models.BooleanField(default=True) date_created = models.DateTimeField(auto_now_add=True) def __str__(self): return self.text # new class Category(models.Model): name = models.CharField(max_length=100) is_active = models.BooleanField(default=False) def __str__(self): return self.name class Meta: verbose_name_plural = 'Categories' |
マイグレーションを実施しましょう。
1 |
make migrate |
カテゴリも管理画面上に表示できるようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment, Category class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories',), # new 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories', ) # new def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) |
管理画面を確認しましょう。Blogの投稿画面からカテゴリを追加できるようにしておきました。

ドロップダウンフィルタ機能の実装
django-admin-list-filter-dropdownにて、ドロップダウンフィルタ機能を追加してみましょう。
settings.pyにdjango-admin-list-filter-dropdownを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
admin.pyに以下を追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter, ChoiceDropdownFilter # new from django_summernote.admin import SummernoteModelAdmin from main.models import Blog, Comment, Category class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 # new list_filter = ( ('blog', RelatedDropdownFilter), ) class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) list_filter = ('is_draft', 'date_created') list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) |
管理画面を確認してみましょう。

rangefilter
django-admin-rangefilterを導入します。
settings.pyにdjango-admin-rangefilterを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', 'rangefilter', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
admin.pyを修正しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_admin_listfilter_dropdown.filters import DropdownFilter, RelatedDropdownFilter, ChoiceDropdownFilter from django_summernote.admin import SummernoteModelAdmin from rangefilter.filter import DateTimeRangeFilter # new from main.models import Blog, Comment, Category class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 list_filter = ( ('blog', RelatedDropdownFilter), ) class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) # Edit list_filter = ('is_draft', ('date_created', DateTimeRangeFilter), ) list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) |
管理画面を確認してみましょう。

Mapの追加
Leafletとdjango-geojsonを使って、Mapを作成してみましょう。
settings.pyにこれらのモジュールを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', 'rangefilter', 'leaflet', # new 'djgeojson', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
Mapモデルを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
vi app/main/models.py from django.db import models from djgeojson.fields import PointField # new class Blog(models.Model): title = models.CharField(max_length=255) body = models.TextField() date_created = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) is_draft = models.BooleanField(default=True) slug = models.SlugField(max_length=100) categories = models.ManyToManyField('main.Category') def __str__(self): return self.title class Comment(models.Model): blog = models.ForeignKey(Blog, related_name='comments', on_delete=models.CASCADE) text = models.TextField() is_active = models.BooleanField(default=True) date_created = models.DateTimeField(auto_now_add=True) def __str__(self): return self.text class Category(models.Model): name = models.CharField(max_length=100) is_active = models.BooleanField(default=False) def __str__(self): return self.name class Meta: verbose_name_plural = 'Categories' class Map(models.Model): name = models.CharField(max_length=255) location = PointField() def __str__(self): return self.name |
マイグレーションを実施しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
make migrate docker-compose run --rm app sh -c "python manage.py makemigrations" Creating network "django-admin_default" with the default driver Creating django-admin_db_1 ... done You are trying to add a non-nullable field 'location' to map without a default; we can't do that (the database needs something to populate existing rows). Please select a fix: 1) Provide a one-off default now (will be set on all existing rows with a null value for this column) 2) Quit, and let me add a default in models.py Select an option: 1 # 1を選択 Please enter the default value now, as valid Python The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now Type 'exit' to exit this prompt >>> "" # 空 Migrations for 'main': main/migrations/0007_map_location.py - Add field location to map docker-compose run --rm app sh -c "python manage.py migrate" Starting django-admin_db_1 ... done Operations to perform: Apply all migrations: admin, auth, contenttypes, django_summernote, main, sessions Running migrations: Applying main.0007_map_location... OK |
admin.pyを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_admin_listfilter_dropdown.filters import RelatedDropdownFilter # new from leaflet.admin import LeafletGeoAdmin # new from django_summernote.admin import SummernoteModelAdmin from rangefilter.filter import DateTimeRangeFilter from main.models import Blog, Comment, Category, Map # new class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(admin.ModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 list_filter = ( ('blog', RelatedDropdownFilter), ) class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) list_filter = ('is_draft', ('date_created', DateTimeRangeFilter), ) list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) admin.site.register(Map, LeafletGeoAdmin) # new |
管理画面を表示させてみましょう。


インポート・エクスポート機能
管理画面にインポート・エクスポート機能を付けてみましょう。
settings.pyにモジュールを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', 'rangefilter', 'leaflet', 'djgeojson', 'import_export', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
mainアプリケーション配下にresources.pyを作成しましょう。
1 |
touch app/main/resources.py |
今回は、とりあえずコメントをインポート・エクスポートできるようにします。
1 2 3 4 5 6 7 8 9 |
from import_export import resources from main.models import Comment class CommentResource(resources.ModelResource): class Meta: model = Comment |
admin.pyを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_admin_listfilter_dropdown.filters import RelatedDropdownFilter from leaflet.admin import LeafletGeoAdmin from django_summernote.admin import SummernoteModelAdmin from import_export.admin import ImportExportModelAdmin # new from rangefilter.filter import DateTimeRangeFilter from main.models import Blog, Comment, Category, Map from main.resources import CommentResource # new class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(ImportExportModelAdmin): # new list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 list_filter = ( ('blog', RelatedDropdownFilter), ) resource_class = CommentResource # new class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) list_filter = ('is_draft', ('date_created', DateTimeRangeFilter), ) list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) admin.site.register(Map, LeafletGeoAdmin) |
管理画面を確認してみましょう。

管理画面のデフォルトのレイアウトを変更する
Django Grappelliを使って、管理画面のレイアウトを変更してみましょう。
settings.pyにモジュールを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', 'rangefilter', 'leaflet', 'djgeojson', 'import_export', 'grappelli', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
app/urls.pyに次のパスを追加しましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
vi app/app/urls.py from django.contrib import admin from django.conf import settings from django.conf.urls.static import static from django.urls import include, path admin.site.site_header = 'SELF NOTE' # Change title admin.site.site_title = 'SELF NOTE' # Change URL title admin.site.index_title = 'SELF NOTE ADMINISTRATION' # Change title2 urlpatterns = [ path('admin/', admin.site.urls), path('summernote/', include('django_summernote.urls')), path('grappelli/', include('grappelli.urls')), # new ] # https://docs.djangoproject.com/en/1.11/topics/files/ if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
管理画面を確認してみましょう。

削除ボタンの無効化
削除ボタンを無効化できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
vi app/main/admin.py from django.contrib import admin from django.db.models import Count from django.utils import timezone from django_admin_listfilter_dropdown.filters import RelatedDropdownFilter from leaflet.admin import LeafletGeoAdmin from django_summernote.admin import SummernoteModelAdmin from import_export.admin import ImportExportModelAdmin from rangefilter.filter import DateTimeRangeFilter from main.models import Blog, Comment, Category, Map from main.resources import CommentResource class CommentInline(admin.StackedInline): model = Comment fields = ('text', 'is_active') extra = 1 classes = ('collapse', ) class CommentAdmin(ImportExportModelAdmin): list_display = ('blog', 'text', 'date_created', 'is_active') list_editable = ('text', 'is_active',) list_per_page = 20 list_filter = ( ('blog', RelatedDropdownFilter), ) resource_class = CommentResource list_select_related = ('blog', ) raw_id_fields = ('blog', ) class BlogAdmin(SummernoteModelAdmin): list_display = ('title', 'date_created', 'last_modified', 'is_draft', 'days_since_creation', 'no_of_comments',) list_filter = ('is_draft', ('date_created', DateTimeRangeFilter), ) list_per_page = 50 actions = ('set_blogs_to_published', ) date_hierarchy = 'date_created' fieldsets = ( (None, { 'fields': (('title', 'slug'), 'body'), }), ('Advanced options', { 'fields': ('is_draft', 'categories', ), 'description': 'Opstions to configure blog creation', 'classes': ('collapse',), }) ) search_fields = ('title',) prepopulated_fields = {'slug': ('title', )} summernote_fields = ('body',) inlines = (CommentInline,) filter_horizontal = ('categories',) def days_since_creation(self, blog): """diff date to show on the list""" diff = timezone.now() - blog.date_created return diff.days days_since_creation.short_description = 'DAY ACTIVE' def get_ordering(self, request): if request.user.is_superuser: return('title', '-date_created') return ('title',) def get_queryset(self, request): queryset = super().get_queryset(request) queryset = queryset.annotate(comments_count=Count('comments')) return queryset def no_of_comments(self, blog): return blog.comments_count no_of_comments.admin_order_field = 'comments_count' def set_blogs_to_published(self, request, queryset): """Set Action Behave""" count = queryset.update(is_draft=False) self.message_user(request, '{} blogs have been published successfully!'.format(count)) set_blogs_to_published.short_description = 'Mark selected blog as published' # new def get_actions(self, request): actions = super().get_actions(request) try: del actions['delete_selected'] except KeyError: pass return actions # new def has_delete_permission(self, request, obj=None): return False admin.site.register(Blog, BlogAdmin) admin.site.register(Comment, CommentAdmin) admin.site.register(Category) admin.site.register(Map, LeafletGeoAdmin) |
管理画面を確認しましょう。

削除ボタンが消えていることがわかると思います。
ハニーポットの設置
最後にハニーポットを設置して終わりにしましょう。
settings.pyにモジュールを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
vi app/app/settings.py INSTALLED_APPS = [ # local 'main.apps.MainConfig', # 3rd-party 'django_summernote', 'django_admin_listfilter_dropdown', 'rangefilter', 'leaflet', 'djgeojson', 'import_export', 'grappelli', 'admin_honeypot', # new 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] |
app/urls.pyを修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
vi app/app/urls.py from django.contrib import admin from django.conf import settings from django.conf.urls.static import static from django.urls import include, path admin.site.site_header = 'SELF NOTE' # Change title admin.site.site_title = 'SELF NOTE' # Change URL title admin.site.index_title = 'SELF NOTE ADMINISTRATION' # Change title2 urlpatterns = [ path('summernote/', include('django_summernote.urls')), path('grappelli/', include('grappelli.urls')), path('admin/', include('admin_honeypot.urls', namespace='admin_honeypot')), # new path('secret/', admin.site.urls), # new ] # https://docs.djangoproject.com/en/1.11/topics/files/ if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) |
マイグレーションを実行しましょう。
1 |
make migrate |
この設定を行うと通常の管理画面のパス「localhost:8000/admin」が「ハニーポット画面(ダミー画面)」に遷移し、「localhost:8000/secret」パスが通常の管理画面のパスになります。
「localhost:8000/admin」にアクセスし、ログインしようとしてください。
ログインできないはずです。

それから「localhost:8000/secret」にアクセスし、「Login attempts」を確認してみてください。


ログインしようとしたユーザーの一覧が確認できると思います。
あとがき
いかがだったでしょうか?
Djangoの管理画面は、3rd-Party製のモジュールを加えると機能が充実していくことがわかると思います。
しかし、紹介した機能は、まだまだ極一部です。
色々な機能を試してみてください^^
それでは、またっ!
コメントを残す
コメントを投稿するにはログインしてください。