サイドバーの「Django ドキュメント検索」というプレースホルダの入った検索では、Django ドキュメント (Django 2.1) の公式サイト内を検索できます
Django チュートリアルの進め方 その 2 (2)

目次

はじめに

Django を学習するには、何はともあれ公式チュートリアル「はじめての Django アプリ作成」からスタートしてみるのが良いでしょう。しかし、いざチュートリアル通りに進めようとしても躓く場合も多いと思います。そこで、「チュートリアルの進め方」シリーズとして、チュートリアルに沿った作業を行いながら補足事項をまとめることにしました。チュートリアルで躓いた際のヒントとしてお使いください。

利用環境は Ubuntu、 Linux Mint を中心として説明しますが、 Windows についても補足します。

前回は、Django チュートリアルの進め方 その 2 (1) と題してはじめての Django アプリ作成、その 2 の前半についてまとめました。

今回は、はじめての Django アプリ作成、その 2 の中盤についてまとめました。(前後編でまとめるつもりでしたが、この章は分量が多いので3本立てになりました。)

API で遊んでみる

シェルを起動します。表示される文字列は環境によって異なります。

$ python manage.py shell
Python 3.6.5 (default, Apr  1 2018, 05:46:30) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

polls のモデルをインポートします。

>>> from polls.models import Choice, Question

オブジェクトを取得する

モデルクラスの objects 属性を使ってそのモデルの Manager を取得できます。Manager から QuerySet (レコードを表すオブジェクト) を取得できます。

>>> Question.objects
<django.db.models.manager.Manager object at 0x7fa3cb731390>
>>> Question.objects.all()
<QuerySet []>

Question に新しいデータを追加するには、Question のインスタンスを作成して save() を実行します。

>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
>>> q.save()

作成したオブジェクトを確認してみます。

>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2018, 9, 4, 21, 22, 20, 801198, tzinfo=<UTC>)


id フィールドは Django が自動生成したプライマリーキーのフィールドです。(自動インクリメントのプライマリーキーフィールド参照)

question_text, pub_date は、 Qusetion に定義したフィールドでした。それぞれ、質問内容と公開日を表しています。

 

データを更新する

このオブジェクトを使って、データを変更することもできます。

>>> q.question_text = "What's up?"
>>> q.save()
>>> q.id
1
>>> q.question_text
"What's up?"
>>> q.pub_date
datetime.datetime(2018, 9, 4, 21, 22, 20, 801198, tzinfo=<UTC>)
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>

id が 1 のままですから、もとのデータであることがわかります。また、question_text のみが修正されて pub_date はそのままであることも確認できます。10行目は、Question.objects.all (Question の全オブジェクト) の文字列表現になります。

モデルに __str__() を定義する

もう少し詳細な情報があると便利ですので、 Python の特殊メソッドである __str__() をモデルに定義することによって実現します。

from django.db import models


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

ソースの変更を反映するためシェルを起動しなおしてから、objects.all() を確認してみます。(チュートリアルではカスタムメソッド was_published_recently も追加していますが、一旦後回しにします。)

>>> exit();
$ python manage.py shell
>>> from polls.models import Choice, Question
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>

5行目の表現が id 番号でなく ,question_text の内容になったので、識別しやすくなりました。Django には管理画面の自動生成機能があり、管理画面でモデルを表示する際にも __str__ が利用されるため、ここ大事モデルには __str__() を定義するようにしましょう。

では、チュートリアルに戻って、 Question モデルにカスタムメソッド was_published_recently() を追加します。

import datetime

from django.db import models
from django.utils import timezone


class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

    def __str__(self):
        return self.question_text

    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

    def __str__(self):
        return self.choice_text

再度、シェルを起動し直します。

>>> exit();
$ python manage.py shell
>>> from polls.models import Choice, Question
>>>

フィルタを使う

フィルタを試してみます。

>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(id=2)
<QuerySet []>

filter に「フィールド名=値」を渡すことによって、絞り込みができます。id=2 に該当するデータが無いことも確認できます。

次は文字列の前方一致検索です。

>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='When')
<QuerySet []>

「フィールド名__startwith=検索値」とする (フィールド名と startwith の間はアンダースコア2つです。) 事で、フィールド値が検索値から始まるデータだけに絞り込みできます。この例では、'When' で始まるデータは無いことも確認できます。 filter へ渡す引数の詳細は、フィールドルックアップを参照してください。

get() を使う

次は get() を使ってデータを取得してみます。

>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>


get をつかった場合もフィールドルックアップを使って絞り込みができる事が確認できました。filter と get の違いは何でしょうか。
存在しない id=2 の場合で比較してみます。

>>> Question.objects.filter(id=2)
<QuerySet []>
>>> Question.objects.get(id=2)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/jade/env/djtutorial/lib/python3.6/site-packages/django/db/models/manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/jade/env/djtutorial/lib/python3.6/site-packages/django/db/models/query.py", line 399, in get
    self.model._meta.object_name
polls.models.Question.DoesNotExist: Question matching query does not exist.

get を使用した 3 〜 10行目は DoesNotExist 例外が発生しました。 filter は存在しないオブジェクトの場合は空の QuerySet が返ってきますが、get は例外が発生するという事ですね。

プライマリーキーへのアクセスは id の代わりに pk を使う事ができます。

>>> Question.objects.get(pk=1)
<Question: What's up?>
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True

データを登録する

次は pk=1 の Question に紐付いた Choice を登録してみます。

>>> q.choice_set.all()
<QuerySet []>
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
>>> c.question
<Question: What's up?>
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
3

create でオブジェクトを作成し、all で作成したオブジェクト一覧を取得、count で個数を取得しています。
Choice へのフィルタと削除を試します。

>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
(1, {'polls.Choice': 1})
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>]>

3行目で choice_text が 'Just hacking' で始まる Choice を取得して削除しています。

今回は、シェルを使って Django のモデル API の動作を確認しました。

次回は、Django チュートリアルの進め方 その 2 (3) です。

参考

 

低価格なのに高速・多機能・高セキュリティ 月額400円(税別)から最大容量100GB WordPress専用高速サーバー Z.com WP
おすすめの記事