※このブログではサーバー運用、技術の検証等の費用のため広告をいれています。
記事が見づらいなどの問題がありましたらContactからお知らせください。

<前のページ
【初心者チュートリアル】Django2でブログ作成(Part5)〜管理サイトを使おう~
次のページ>
【初心者チュートリアル】Django2でブログ作成(Part7)〜ModelManager~

【初心者チュートリアル】Django2でブログ作成(Part6)〜QuerySet(練習編)~

web開発 python3 Django Djangoチュートリアル pyhton

投稿日:2019年11月9日

このエントリーをはてなブックマークに追加
Djangoは簡単にWebアプリケーションを作成できるフレームワークです。この記事は初心者の方向けのDjangoチュートリアルです。

はじめに

前回はDjangoの管理サイトでテーブルのレコードを操作しました。

今回の記事はそのQuerySetの解説とその使い方の練習で実際にコードは書きません。


QuerySet

QuerySetについて

実際にブログを運営するときには、ユーザーがサイトのURLにアクセスした時に、自動で適したテーブルのレコードを触る必要があります。

QuerySetはプログラムからテーブルのレコードを操作するためのDjangoのORM(Object-Relational Mapper)のAPIです。

ORMのAPIを使うことでそれぞれのデータベース(MySQL、PostgresSQL、SQLite、...)ごとのコードに違いを埋めることができます。どのDBを使用するかはsettings.pyDATABASESの変数で設定されます。覚えておきましょう!

Djangoのshellを使おう

ここではQuerySetを試しに対話的にさわって行くのですが、pythonのshellではなくDjangoのshellを使います。

Djangoのプロジェクトで作成したコードはそのままpythonのshellではちゃんと動きません。

pythonのshellではちゃんと動かない
$ python
Python 3.6.8 (default, Oct  7 2019, 12:59:55) 
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from blog.models import Article
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ogihara/django_tutorial/sample_blog/blog/models.py", line 3, in <module>
    from django.contrib.auth.models import User
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/contrib/auth/models.py", line 2, in <module>
    from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/contrib/auth/base_user.py", line 47, in <module>
    class AbstractBaseUser(models.Model):
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/db/models/base.py", line 103, in __new__
    app_config = apps.get_containing_app_config(module)
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/apps/registry.py", line 252, in get_containing_app_config
    self.check_apps_ready()
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/apps/registry.py", line 134, in check_apps_ready
    settings.INSTALLED_APPS
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/conf/__init__.py", line 79, in __getattr__
    self._setup(name)
  File "/home/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/conf/__init__.py", line 64, in _setup
    % (desc, ENVIRONMENT_VARIABLE))
django.core.exceptions.ImproperlyConfigured: Requested setting INSTALLED_APPS, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.

作成したDjangoのクラスや関数をコマンドラインなどから使いたい場合は、Djangoのshellを使いましょう。
Djangoのshellは以下のコマンドから起動できます。

ターミナル
$ python manage.py shell        # DjangoのShellの起動

manage.pyのshellは開いたまま次にすすんでください

レコードを作成

QuerySetでレコードを作成してみましょう。

ターミナル
>>> from django.contrib.auth.models import User
>>> from blog.models import Article
>>> user = User.objects.get(username="marsquai")
>>> article = Article(title = "シェル記事", body="この記事はシェルで書かれました。", author=user)
>>> article.save()

管理サイトで確認するとこの様にちゃんとレコードの追加ができています。

簡単に解説します。

まずは前にcreatesuperuserで作成した管理者のUserレコードを取得するための以下のコードです。

user = User.objects.get(username="marsquai")

この様に、get()メソッドは条件を指定してテーブルからレコードを1つだけ取得することができます。このメソッドは条件に一致するレコードが必ず1つ存在することを前提としています。存在しなかった場合DoesNotExitというexception、複数存在した場合にはMultipleObjectReturnedというexceptionをなげられます。

存在しない場合exceptionを投げる
>>> user = User.objects.get(username="aaaaaaaa")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/ogihara/django_tutorial/myvenv/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/ogihara/django_tutorial/myvenv/lib/python3.6/site-packages/django/db/models/query.py", line 408, in get
    self.model._meta.object_name
django.contrib.auth.models.User.DoesNotExist: User matching query does not exist.

なるほど、プログラム中でget()メソッドを単体で使う時にはtry-expectでエラーの管理をしっかりしなくてはいけませんね。

次にArticleクラスのインスタンスを作成している以下の部分です。

article = Article(title = "シェル記事", body="この記事はシェルで書かれました。", author=user)

引数に作成したいレコードのフィールドの値をいれます。

authorのようにmodels.ForeignKeyで定義した値は、get()メソッドで取得したインスタンスを渡しています。

インスタンスの生成の段階ではまだこのデータはメモリ上に乗っているだけでデータベースに変更が適用されていません。

次のsave()メソッドがながれて初めてテーブルに変更が適用されます。

ここでDBに変更が適用
article.save()

これは内部的にSQLのINSERT文が走っています。

この方法は一端メモリにオブジェクトを作成してそれをデータベースに永続化させるのですが、create()メソッドを使うとそれを一度に行うことができます。

create()メソッドで作成する場合
Article.objects.create(title = "シェル記事", body="この記事はシェルで書かれました。", author=user)

レコードを作成するだけならこっちのほうが簡単ですね。

レコードの変更

インスタンスの変数の値を変えてsave()メソッドを呼ぶことでレコードの変更をすることができます。

ターミナル
...
>>> article.title="Shellから変更された記事"
>>> article.save()

変数を変えたときにもsave()が呼ばれるまでデータベースに適用されません。変更の際にはsave()メソッドの時内部で実行されるSQLのUPDATE文が流れます。

レコードの全件取得

全てのレコードのオブジェクトはall()メソッドで取得できます。

ターミナル
...
>>> article_all = Article.objects.all()
>>> print(article_all)
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 君にもできるスクレイピング!>, <Article: Shellから変更された記事>, <Article: シェル記事>, <Article: 今夜の料理は?>, <Article: 雑談日記>]>

レコードのフィルタリング(基礎)

filter()メソッドを使うことで条件を指定してその条件を満たすレコードのオブジェクトを取得できます。

例えば公開状態が公開中のもののみ取得する場合:

ターミナル
...
>>> articles = Article.objects.filter(status="published")
>>> articles
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 今夜の料理は?>]>

レコードのフィルタリング(応用その1)

レコードの取得は__(ダブルハイフン)を使うことで細かい指定ができます。

例えば、以下の様にすると公開日(DateTime)の年だけからフィルタリングできます。

ターミナル
...
>>> articles = Article.objects.filter(publish__year=2019)
>>> articles
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 君にもできるスクレイピング!>, <Article: Shellから変更された記事>, <Article: シェル記事>, <Article: 今夜の料理は?>, <Article: 雑談日記>]>

外部キーの場合そのテーブルのフィールドも__(ダブルハイフン)で指定できます。

ターミナル
...
>>> articles = Article.objects.filter(author__username="marsquai")
>>> articles
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 君にもできるスクレイピング!>, <Article: Shellから変更された記事>, <Article: シェル記事>, <Article: 今夜の料理は?>, <Article: 雑談日記>]>

この__(ダブルハイフン)を使った条件指定は他にも指定した値以下のレコードを取得する__lteや、指定した値以上のレコードを取得する__gte、その他にもいろんな便利な指定方法があるので興味があれば調べてみましょう。

レコードのフィルタリング(応用その2)

レコードのフィルタリングは複数の条件を指定することができます。複数の条件を指定する場合はfilter()メソッドをつなげるか、filter()メソッドの引数に指定したい条件をすべて渡してあげればOKです。

ターミナル
>>> articles = Article.objects.filter(author__username="marsquai", publish__year=2019)
>>> articles
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 君にもできるスクレイピング!>, <Article: Shellから変更された記事>, <Article: シェル記事>, <Article: 今夜の料理は?>, <Article: 雑談日記>]>

レコードのフィルタリング(応用その3)

exclude()メソッドは指定した条件を満たさないレコードのObjectを検索できます。

ターミナル
>>> articles = Article.objects.exclude(status="draft")
>>> articles
<QuerySet [<Article: エンジニアの健康維持の秘訣とは?>, <Article: 今夜の料理は?>]>

順番を指定したフィルタリング

order_by()メソッドを使うとレコードのオブジェクトを特定のフィールドごとに並び替えて取得できます。

例えば全てのレコードをタイトル順に並び替える場合:

ターミナル
>>> articles = Article.objects.order_by("title")
>>> articles
<QuerySet [<Article: Shellから変更された記事>, <Article: エンジニアの健康維持の秘訣とは?>, <Article: シェル記事>, <Article: 今夜の料理は?>, <Article: 君にもできるスクレイピング!>, <Article: 雑談日記>]>

渡す引数のフィールド名に - をつければ逆順に並べることができます。

ターミナル
>>> articles = Article.objects.order_by("-title")
>>> articles
<QuerySet [<Article: 雑談日記>, <Article: 君にもできるスクレイピング!>, <Article: 今夜の料理は?>, <Article: シェル記事>, <Article: エンジニアの健康維持の秘訣とは?>, <Article: Shellから変更された記事>]>

オブジェクトの並び順の優先順位は以下の順になっています。

  1. order_by()メソッドで指定した並び順
  2. ModelのMetaクラスのorderingで指定した順
  3. Modelのid

レコードの削除

レコードの削除にはオブジェクトのインスタンスのdelete()メソッドを呼び出します。

ターミナル
>>> article = Article.objects.get(id=1)
>>> article.delete()
(1, {'blog.Article': 1})

さいごに

大体のQuerySetの使い方はこの記事で紹介できました。
詳しい使い方は公式ドキュメントを参照しましょう。

公式ドキュメント QuerySet:https://docs.djangoproject.com/ja/2.2/ref/models/querysets/

公式ドキュメント Queryhttps://docs.djangoproject.com/ja/2.2/topics/db/queries/

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

<前のページ
【初心者チュートリアル】Django2でブログ作成(Part5)〜管理サイトを使おう~
次のページ>
【初心者チュートリアル】Django2でブログ作成(Part7)〜ModelManager~

関連記事

記事へのコメント