PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（５）【Django shellでDB操作入門編】

# 解説記事の構成

|No.|タイトル||
|:--:|:--|:--:|
|1|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（１）【環境構築編】](http://qiita.com/carat_yoshizaki/items/f3c79e38b802c7fc8863)||
|2|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（２）【プロジェクト作成編】] (http://qiita.com/carat_yoshizaki/items/926f702198bdfe8c6bd2)||
|3|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（３）【アプリケーション作成・DB設定編】] (http://qiita.com/carat_yoshizaki/items/c9a5299b77b99ff07e4a)||
|4|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（４）【ルーティング設定・MTVデザインパターン入門編】](http://qiita.com/carat_yoshizaki/items/fbd235008d7484089270)||
|5|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（５）【Django shellでDB操作入門編】](http://qiita.com/carat_yoshizaki/items/8c57144dfd37e005fb17#_reference-8f82e1176420484c2fdd)|←|
|6|[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（６）【MTVデザインパターン完成編】](http://qiita.com/carat_yoshizaki/items/f0b4f7d7fa3899f2d424)||


# 開発環境

* OS: Mac OS Sierra (10.12)
* Python 3.5.2

Macの環境を前提として話を進めていきます。

Pythonのバージョン確認は以下の通りです。

```bash
$ python3 --version
Python 3.5.2
```


# Django shell

今回は ```Django shell``` の使い方を紹介します。

前回は、Django adminを使って、DBを操作したり確認したりする方法を紹介しました。
Ruby on Railsに慣れている方は、そんなのコンソールの ```rails c``` で操作したり確認したりすれば良いんじゃないの？と思われる方もいらっしゃると思いますが、その通りかと思います。
Djangoもそういったコンソールを備えており、それが ```Django shell``` というわけです。

## Django shellを使ってみよう

まず、```Django shell``` を立ち上げます。

```bash:Djangoの起動
$ python3 manage.py shell
Python 3.5.2 (default, Jun 29 2016, 13:43:58) 
[GCC 4.2.1 Compatible Apple LLVM 7.3.0 (clang-703.0.31)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 
```

## Django shellでデータを表示させよう

では、早速定義したモデルである ```Post``` のデータを見てみましょう。

```bash
>>> Post.objects.all()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'Post' is not defined
```
残念ながらエラーが出てしまいました（Ruby on Rails同様の手順でならエラーは出ないのに）。
Djangoでは、最初にモデルをインポートしないといけないようです。

```bash
>>> from blog.models import Post
>>> Post.objects.all()
<QuerySet [<Post: サンプル記事のタイトル>]>
```
これでDB内のデータを確認することが出来増しtあ。
```QuerySet``` という名前が出てきていますが、クエリセットとはモデルが提供しているオブジェクトのリストのことだそうです。

## Coffee Break：DjangoとRuby on Railsを比べてみる

これまでの記事を読まれている方はお気づきかも知れませんが、私は Ruby on Rails を一番最初のWebフレームワークとして学習し、それが自分の中のスタンダードになっているため、Djangoはところどころで、Ruby on Railsに比べると使いにくいなという印象を受けています。
しかし、機械学習を使用するためには、PythonのWebフレームワークは非常に魅力的です。
個人的には、Ruby on RailsでWebアプリケーションの土台を作り、DjangoでWeb APIを作るのがオススメです。


## Django shellでobjectを作ろう

データベースに新しい ```Post``` を作成します。

```bash
>>> Post.objects.create(author=ryosuke, title="ryosuke's article", text="これはQiita用の記事です")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
NameError: name 'ryosuke' is not defined
```
エラーが出ましたね。
```author=ryosuke``` でエラーが起きました。
```author``` って指定した名前で勝手に増えるものではないのですね。。。

このあたりは、実は Django でモデルを作った際（[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（３）：モデルの作成](http://qiita.com/carat_yoshizaki/items/c9a5299b77b99ff07e4a#%E3%83%A2%E3%83%87%E3%83%AB%E3%81%AE%E4%BD%9C%E6%88%90)）に、 ```blog/models.py``` 内で

```py:blog/models.pyの一部
author = models.ForeignKey('auth.User')
```

として、Userモデルを参照するように設定しているためです。
ここを単純に文字列として定義しておけば、新規の名前に対しても問題なくオブジェクトを作成できるというわけです。
しかし、これではユーザーの名前の管理が難しくなるので、ユーザーはユーザー、ポストはポストとして管理して、ポストからユーザーを参照するようにしています。

今回は新しくユーザーを作ることはせず、```ryosuke``` というユーザーに ```admin``` の情報を与え、そこから新規オブジェクトを作成していきます。

```bash:ryosukeにadminの情報を与える
>>> from django.contrib.auth.models import User
>>> User.objects.all()
<QuerySet [<User: admin>]>
>>> ryosuke = User.objects.get(username='admin')
>>> ryosuke
<User: admin>
```

```bash:Postに新規オブジェクトを作成
>>> Post.objects.create(author=ryosuke, title="ryosuke's article", text="これはQiita用の記事です")
<Post: ryosuke's article>
```

これで上手くいきました。
Postのオブジェクトリストを見てみましょう。

```bash
>>> Post.objects.all()
<QuerySet [<Post: サンプル記事のタイトル>, <Post: ryosuke's article>]>
```
新しい```Post```が増えていることが分かると思います。


##  Django shellで特定条件のオブジェクトを抽出しよう

特定条件を抽出する場合は ```all()``` であった部分を ```filter()``` を使い、引数にプロパティの情報を与えます。
例えば、```author=ryosuke``` のPostのみ抽出する場合は以下のようになります。

```bash
>>> Post.objects.filter(author=ryosuke)
<QuerySet [<Post: サンプル記事のタイトル>, <Post: ryosuke's article>]>
```

今の所、全てのオブジェクトと変わりがありませんが、```author```が増えてきた場合は、このようにして、著者ごとにわけることができます。

完全に一致する条件の抽出だけでなく、一部に特定の文字列を含むような部分一致の検索もできます。

```bash:titleにarticleを含むオブジェクトのみ抽出
>>> Post.objects.filter(title__contains='article')
<QuerySet [<Post: ryosuke's article>]>
```

```title``` に ```__contains``` を付け加えることで、部分一致も上手く扱えるようになります（これ結構便利です）。

Pythonでは、上記の用にアンダーバーが２つ続くことがよくあります。
参考：[Django Girls Tutorial：クエリセット１](https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/content/django_orm/)
>
*Note*
title と contains の間に、アンダーバーが2個続いていますが、これはDjangoのORMの構文です。フィールド名のtitleと、照合タイプのcontainsを、2つのアンダーバーで連結させています。もしアンダーバーが1個だけだと、title_contains というフィールド名だと判断されてしまい、エラーになります。("FieldError: Cannot resolve keyword title_contains")

つぎに、これもよく使う機能ですが、既に公開済みの記事のみを抽出する方法です。
Qiitaでも記事の作成はしますが、下書きで一旦置いておく場合ありますよね。
この現時刻で、公開済みのみを抽出したい場合の方法です。

```bash
>>> from django.utils import timezone
>>> Post.objects.filter(published_date__lte=timezone.now())
<QuerySet [<Post: サンプル記事のタイトル>]>
```

勘の鋭い方ならお分かりかもしれませんが、```lte``` の部分が ```contain``` 同様、フィルターを書ける際の条件分になっています。
```lte``` は ```less than or equal to```の略で、数学的な記号では 「published_date <= 現時刻」ということになります。
現時刻より前もしくは現時刻と一致した場合は抽出するといった条件分ですね。
先ほどDjango shellから追加したオブジェクトは ```published_date``` を設定していなかったので、今回の条件には該当せずとなっています。

変数名にはひとつのアンダーバーを使うことが多い為、DjangoのORMの構文にはアンダーバー二つ続きを採用しています。アンダーバーが一つだと変数名として扱われてしまうため```"FieldError: Cannot resolve keyword title_contains"```エラーとなります。

## オブジェクトの内容を更新

さきほど作成したオブジェクトがまだ公開 (```publish```) されていなかったので、これを公開していきましょう。

```bash:作成したモデルの取得
post = Post.objects.get(id=2)
>>> post
<Post: ryosuke's article>
```

```blog/models.py``` で ```publish()``` メソッドを以下のように定義しているため、このメソッドを使えば、簡単に ```published_date``` の値を更新することができます。

```py:blog/models.py
def publish(self):
    self.published_date = timezone.now()
    self.save()
```

では、この```publish()```メソッドを使っていきます。

```bash
>>> post.publish()
>>> Post.objects.filter(published_date__lte=timezone.now())
<QuerySet [<Post: サンプル記事のタイトル>, <Post: ryosuke's article>]>
```

これで、無事記事を公開することが出来ました。


# オブジェクトのリストの並べ替え

最後に簡単に並べかえの使い方を知っておきましょう。
Webページでは、金額や日時による並べ替えをすることが多々有り、よく使うコマンドになると思います。

並べ替えには ```order_by()``` を使用します。

```
>>> Post.objects.order_by('created_date')
<QuerySet [<Post: サンプル記事のタイトル>, <Post: ryosuke's article>]>
```

基本的には、日時であれば古い順に並びます。
新しく追加した順に並び替えたい場合は、この逆順を以下のように指定すればできます。

```bash
>>> Post.objects.order_by('-created_date')
<QuerySet [<Post: ryosuke's article>, <Post: サンプル記事のタイトル>]>
```

なんと、```order_by``` の引数に ```-``` を付けるという発想。
```asc``` や ```desc``` ではないのですね（たぶんこの辺りもサポートしてると思いますが）。

これで並べ替えも出来るようになりました。

# Django shellの終了

コンソールを立ち上げたものの、最初は終了の仕方がよく分からないといったことがあります。

```bash
>>> exit()
```

この最後の ```()``` もお忘れ無く。

# 次回の解説記事
[PythonのWebアプリケーション（Django）を初心者にもわかりやすく解説（６）【MTVデザインパターン完成編】](http://qiita.com/carat_yoshizaki/items/f0b4f7d7fa3899f2d424)


# 参考文献

* [Django](https://www.djangoproject.com/)
* [Django Girls Tutorial「クエリセット１」](https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/content/dynamic_data_in_templates/)

# おまけ

フォローお待ちしています！

* Qiita: [Carat 吉崎](http://qiita.com/carat_yoshizaki)
* twitter：[@carat_yoshizaki](https://twitter.com/carat_yoshizaki)
* はてなブログ：[Carat COOのブログ](http://carat-tech.hatenablog.com/)
* ホームページ：[Carat](http://caratinc.jp)

サービス紹介
「数学→プログラミング→Webアプリケーション」まで一気に学べる機械学習のマンツーマン家庭教師サービス「[キカガク](https://kikagaku.net)」に興味のある方はお気軽にご連絡ください。