Djangoチュートリアルを適当に進める~その7~

2019年3月11日

Djangoのチュートリアルの備忘録。
公式チュートリアルだと以下の部分。
https://docs.djangoproject.com/en/2.1/intro/tutorial07/

adminページ、またの名を管理者ページをカスタマイズする。

adminフォームのカスタマイズ

現在、Questionモデルを使ってデータベースにデータを登録する際、
admin.site.register(Question) を使っている。
これはチュートリアル2で書いたコードだ。

Djangoチュートリアルを適当に進める~その2~

ちなみに現在の見た目はこう。

この入力フォームを変えたいときはオプションを使う。
admin.pyをこういじる。

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fields = ['pub_date', 'question_text']

admin.site.register(Question, QuestionAdmin)

これでサーバーを始動させてhttp://localhost:8000/admin/にアクセスしてみよう。
と思ったのだが、ユーザー名とパスワードを忘れてしまった・・・・
そんな時は、shellを立ち上げてデータベースを直接いじって解決。

In [1]: from django.contrib.auth.models import User                 

In [2]: users = User.objects.all()    

In [3]: user = users[0]               
# まずはユーザー名を確認だ。"admin"なんてユーザー名は良くないね。
In [4]: user                          
Out[4]: <User: admin>
# パスワードをセットしよう。間違っても下の例のように"password"にしてはいけない。
In [5]: user.set_password('password') 

In [6]: user.save()                   

さて、再度気を取り直してサーバーを立ち上げてログイン!

こんな画面が出るハズ。

まあ、順番がいれかわっただけ。
でも、フィールドがいっぱいあるときは、入力の順番を指定したいことが多々あるハズ。
より複雑なデータ入力を行う場合、fieldsetsを使うとグルーピングができる。

from django.contrib import admin

from .models import Question


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date']}),
    ]

admin.site.register(Question, QuestionAdmin)

Data informationというタイトルがついた。

関係するオブジェクトを追加

QuestionにはChoice(選択肢)が付属していたので、それも登録できるようにする。
二つ方法があるが、一つ目はシンプルにこんなかんじ。

from django.contrib import admin

from .models import Choice, Question
# 最下部にこれを足す
admin.site.register(Choice)

するとこうなる。

データベースのChoiceをいじれるようになった。
クリックしてみると、保存されているchoiceが出てくる。

ここで右上の「Add choice」をクリックしてみるとこんな絵に。

あと、Questionのセレクトボックスの隣に+ボタンがついた。ここからQuestionを追加することも出来る。
でも、Question作った時に一緒にChoiceを作ってしまえた方が良いってことで、admin.pyに下記コードを追加。

from django.contrib import admin

from .models import Choice, Question


class ChoiceInline(admin.StackedInline):
    model = Choice
    extra = 3


class QuestionAdmin(admin.ModelAdmin):
    fieldsets = [
        (None,               {'fields': ['question_text']}),
        ('Date information', {'fields': ['pub_date'], 'classes': ['collapse']}),
    ]
    inlines = [ChoiceInline]

admin.site.register(Question, QuestionAdmin)

StackedInlineを継承してChoiceInlineクラスを作り、その中にmodelとextraを指定する。
んで、それをQuestionAdminクラスの中のinlinesに代入してあげる。
そしてサーバーを立ち上げるとこんな感じの画面に。

当然のことながら、Choicesはいじれなくなっている。

Questionを追加するときにCHOICESも同時に追加できるようになった。
3つも追加できる。これはextraに設定した数字が反映される。
「Add another Choice」のリンクをクリックすると、下記の画面のようにもう一個入力するところが追加される。
ちょっと場所取りすぎ・・・と思ったら、
class ChoiceInline(admin.StackedInline):

class ChoiceInline(admin.TabularInline):
に代えよう。
するとこうなる。

adminチェンジリストをカスタマイズする

次はこのページの見た目を代えよう。

表示される情報をもっと増やしたい。
admin.pyをこういじる。

class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_display = ('question_text', 'pub_date', 'was_published_recently')

この「was_published_recently」はチュートリアル2で作ったやつ。
どこで使うんだと思っていたが、ここで使うのね。
んでさっきの画面がこうなる。

「QUESTION TEXT」と「DATE PUBLISHED」は項目名をクリックしてソートが出来るが、
「WAS PUBLISHED RECENTLY」は出来ない。
任意のメソッドはソートできないらしい。
また、メソッド名がそのまま表示されている。

class Question(models.Model):
    # ...
    def was_published_recently(self):
        now = timezone.now()
        return now - datetime.timedelta(days=1) <= self.pub_date <= now
    was_published_recently.admin_order_field = 'pub_date'
    was_published_recently.boolean = True
    was_published_recently.short_description = 'Published recently?'

すると、項目名が変わって、なおかつソートできるようになる。

さらに、admin.pyに次の行を加えよう。

class QuestionAdmin(admin.ModelAdmin):
    # ...
    list_filter = ['pub_date']

すると、「pub_date」でフィルターできるようになる。

さらに

さらに、admin.pyに次の行を加えよう。

class QuestionAdmin(admin.ModelAdmin):
    # ...
    search_fields = ['question_text']

検索窓がついたった。

見た目をしゃれおつにする

タイトルのところに「Django administration」って出ているのダサい。
では、polls/templatesフォルダーに新たにadminフォルダーを作ろう。
そこに、django/contrib/admin/templates」から「admin/base_site.htmlを
コピーしてくる。
Djangoのフォルダがどこかわからない場合は、下記コマンドを打とう。
ちなみにコピーするコマンドは「cp」。

$ python -c "import django; print(django.__path__)"

テンプレートのここの部分をかえる。

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">{{ site_header|default:_('Django administration') }}</a></h1>
{% endblock %}

中身をこう変える。

{% block branding %}
<h1 id="site-name"><a href="{% url 'admin:index' %}">Polls Administration</a></h1>
{% endblock %}

こうなる。

このように、templateをコピーしてくればDjangoのadminページは色々カスタマイズできる。