Django+MongoDBを使ってみた
作ったもの
自然言語処理100本ノックの69問目の問題で無駄に凝ってDjango+MongoDBでWebアプリケーションを作成した。
69.Webアプリケーションの作成
ユーザから入力された検索条件に合致するアーティストの情報を表示するWebアプリケーションを作成せよ.アーティスト名,アーティストの別名,タグ等で検索条件を指定し,アーティスト情報のリストをレーティングの高い順などで整列して表示せよ.
ソースはGitHubに置いています。
Django+MongoDBの記事が意外と少なくて苦労したので書きましたが、正直よくわかっていない所もあるので、間違っている箇所があったら指摘していただきたく。題意を満たす動作しか確認できていません。
※2019/02/18 不要な記述が多かったので大幅修正
使用例
- トップ画面
* アーティスト名「Queen」で検索
* タグ「jpop」で検索&「レーティング(平均)」で降順にソート
内容
各種バージョン
OS : macOS High Sierra(10.13.4)
Anaconda : 4.5.11
Python : 3.6.0
pip : 19.0.2
MongoDB : 4.0.4
Django : 2.1.3
Djongo : 1.2.31
手順
基本的に下記サイトを参考にしています。分かりやすかったです。
細かい内容はこちらを見て頂ければ良いかと思います。
本ブログでは自分が書き換えた箇所のみを書いていこうかと。
パッケージ導入
必要なものは当然ですがDjangoと、DjangoでMongoDBを利用するために必要なDjongoをインストールする。
$ pip install django
$ pip install djongo
公式サイトに書いてあるが、Pythonは3.6以上、MongoDBは3.4以上が必要らしいので注意。 nesdis.github.io
また、DjangoでMongoDBを利用するためのパッケージにはdjango-mongodb-engineというものもあるが設定がうまくいかなかったので断念。
プロジェクト作成
$ django-admin startproject NLP_100knoks_69
$ tree NLP_100knoks_69/
NLP_100knoks_69/
├── NLP_100knoks_69
│ ├── __init__.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── manage.py
$ cd NLP_100knoks_69
$ python manage.py startapp webapp
- settings.pyのINSTALLED_APPSに「webapp」を追記
Model作成
とりあえず以下のように作成。
※ここが一番要検討箇所
from djongo import models # Create your models here. class Artist(models.Model): #id = models.IntegerField() gid = models.CharField(max_length=100) name = models.CharField(max_length=30) sort_name = models.CharField(max_length=30) area = models.CharField(max_length=20) aliases = models.ListField(models.EmbeddedModelField('Aliase')) begin = models.EmbeddedModelField('Begin') end = models.EmbeddedModelField('End') tags = models.ListField(models.EmbeddedModelField('Tag')) rating = models.EmbeddedModelField('Rating') objects = models.DjongoManager() class Aliase(models.Model): name = models.CharField(max_length=30) sort_name = models.CharField(max_length=30) class Begin(models.Model): year = models.IntegerField() month = models.IntegerField() date = models.IntegerField() class End(models.Model): year = models.IntegerField() month = models.IntegerField() date = models.IntegerField() class Tag(models.Model): count = models.IntegerField() value = models.CharField(max_length=30) class Rating(models.Model): count = models.IntegerField() value = models.CharField(max_length=100)
- settings.pyにDjongoを使うこととデータベース名を指定
DATABASES = { 'default': { 'ENGINE': 'djongo', 'NAME': 'test_database', } }
- データベースにモデルの反映
$ python manage.py makemigrations $ python manage.py migrate
- データをMongoDBに登録
$ python manage.py shell
下記シェルを実行して登録。
import gzip, json from webapp.models import Artist ipath = '/path/to/artist.json.gz' with gzip.open(ipath+"artist.json.gz", "rt", "utf_8") as f: buf = [] for i,line in enumerate(f): obj = json.loads(line) buf.append(obj) if i % 10000 == 0: Artist.objects.mongo_insert_many(buf) buf = [] Artist.objects.mongo_insert_many(buf)
1つずつ登録すると時間がかかりすぎるので、10000件ごとに登録している。
基本的に元データと同じフィールド名を用いたが、idは普通に使おうとすると下記エラーになった。
ERRORS: webapp.Artist: (models.E004) 'id' can only be used as a field name if the field also sets 'primary_key=True'.
軽く調べたが「id」というキーはデフォルトで利用されているから使えないとか。やむなくidはコメントアウト。IDを用いることはあまりないので問題ないと判断。
View作成
- urls.py
from django.contrib import admin from django.urls import path import webapp.views as webapp_view urlpatterns = [ path('admin/', admin.site.urls), path('artist_list/', webapp_view.ArtistListView.as_view()) ]
- views.py
from django.shortcuts import render from django.views.generic import TemplateView from webapp.models import * # Create your views here. class ArtistListView(TemplateView): template_name = "artist_list.html" def search(self,item = '',content = '',limit = 100): if content == '': artists = Artist.objects.mongo_find() else: if item == 'name': artists = Artist.objects.mongo_find({'name':content}) elif item == 'aliase': artists = Artist.objects.mongo_find({'aliases.name':content}) elif item == 'tag': artists = Artist.objects.mongo_find({'tags.value':content}) limit = artists.count() arts = [] # 100件にしている。全体で921337件あるので表示に時間がかかりすぎる為 for artist in artists[:limit]: art = artist # 別名の整形 if 'aliases' in artist: aliase_name = [] for aliase in artist['aliases']: aliase_name.append(aliase['name']) art['aliases'] = ',\n'.join(aliase_name) # 活動開始日の整形 if 'begin' in artist: begin_date = [] if 'year' in artist['begin']: begin_date.append(str(artist['begin']['year'])) if 'month' in artist['begin']: begin_date.append(str(artist['begin']['month'])) if 'date' in artist['begin']: begin_date.append(str(artist['begin']['date'])) art['begin'] = '/'.join(begin_date) # 活動終了日の整形 if 'end' in artist: end_date = [] if 'year' in artist['end']: end_date.append(str(artist['end']['year'])) if 'month' in artist['end']: end_date.append(str(artist['end']['month'])) if 'date' in artist['end']: end_date.append(str(artist['end']['date'])) art['end'] = '/'.join(end_date) # タグの整形 if 'tags' in artist: tag_contents = [] for tag in artist['tags']: tag_contents.append(tag['value']+':'+ str(tag['count'])) art['tags'] = ',\n'.join(tag_contents) # レーティングの整形 if 'rating' in artist: art['rating_num'] = str(artist['rating']['count']) art['rating_ave'] = str(artist['rating']['value']) arts.append(art) d = { 'objects' : arts } return render(self.request, self.template_name, d) def get(self, request, *args, **kwargs): if request.method == 'GET': if 'search' in request.GET: return self.search(request.GET['search_item'], request.GET['search']) else: return self.search()
HTML
参考サイトにほぼ準拠。ただBootStrapでSB Admin 2は最新版ではだいぶ内容が変わっていたので、下記から以前のバージョンを取得して使用した。全部載せると長くなりすぎるので省略。
Release v3.3.7+1 · BlackrockDigital/startbootstrap-sb-admin-2 · GitHub
最終的なディレクトリ構造
一部省略していますが、最終的に以下のような構造になりました。
$ tree . . ├── NLP_100knoks_69 │ ├── __init__.py │ ├── __pycache__ #以下のファイルを省略 │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── db.sqlite3 ├── manage.py ├── static │ ├── bootstrap #以下のファイルを省略 │ │ ├── dist │ │ ├── js │ │ └── vendor │ └── webapp │ └── css │ └── structure.css └── webapp ├── __init__.py ├── __pycache__ #以下のファイルを省略 ├── admin.py ├── apps.py ├── migrations #以下のファイルを省略 ├── models.py ├── templates │ ├── artist_list.html │ └── base.html ├── tests.py └── views.py
完了
参考サイトではこの後ログイン機能等の実装を行わせていますが、今回は不要なのでここで終了。
Djangoを起動し、localhost:8080/artist_listにアクセスすれば使用できる。
まとめ
せっかくだからとDjangoで作ってみたが、結構面白かった。
正直現状Djangoをあまり活用できている気がしないので、ちゃんと勉強したい。