カテゴリー別アーカイブ: Django

Webフレームワークとして使われるDjango、Flask、Ruby on Rails、Sinatraの動向

このところWebフレームワークを調べていて、実際のところ何が一番使われているのか気になりました。

そこで、Stack Overflowでトレンドを調べてみました。調べたのは、Django、Flask、Ruby on Rails、Sinatraです。

次の図が、Stack Overflowにおける過去8年間の質問の割合いを示しています。青がRuby on Rails、橙色がDjango、緑がFlask、赤がSinatraです。

webframework

なんと、今年2017年にDjangoがRuby on Railsを抜いています。また、Flaskは増加しており着実にユーザーの数を増やしていることがうかがえます。また、Sinatraは拡大してみるとRuby on Railsと同様に減少傾向にあります。

Stack Overflowでの質問の数で、一概にユーザーの数がわかるわけではありませんが、初心者の数はRuby on Railsに比べてDjangoの方が増えているのではないかと思います。

Ruby on Railsが減少傾向にあることが気になります。近くの本屋さんでも、Ruby on Railsの本はあまりありません。とういことは、これからRuby on Railsを勉強しようという人がいないということであり、近い将来Ruby on Railsを使う人が減ってしまうことを意味します。

それに対して、Djangoは一定の数を保っていますし、Flaskは増加傾向にあります。近くの本屋さんでも、Pythonの入門書がたくさん置いてありますし、これから学ぶ人が増えていくのではないかと思います。

現状Ruby on Railsのプロジェクトは多いですし、スタートアップ企業では使われていくでしょう。しかし、これからのことを考えるとPythonが確実に広まっていくのかなと思います。

Django Girls Tutorialをやり終えて

Django Girls Tutorialをやり終えました。

このTutorialの良いところは、Webアプリケーションを作る過程を一通りできることです。

作ったアプリケーションをHerokuで公開するところまで含まれていますので、簡単そうでしたが結構骨が折れました。

実際にやってみないとわからないことが多くありました。

Djangoを触ってみて一番驚いたのは、データの削除をするとき関連するデータまで削除してくれることです。

Tutorialの中で、ブログの記事とコメントを作るのですが、記事を削除すると関連する複数あったコメントまで同時に削除してくれます。データベースを見て確認しました。

自分で関連するコメントを選別して削除する必要はありませんでした。

データベースをきれいに保つには欠かせないと思います。

困った点は、Django Girls Tutorialの日本語版はバージョンが古いらしく、英語版のDjango Girls Tutorial:extensionsをやろうとすると、修正が必要だったことです。ただ、その原因を突きとめる過程で学ぶことが多くありました。

PythonでWebアプリケーションを作りたいとなったときに、Djangoを最初に学ぶには良いTutorialではないかと思います。

Django Girls Tutorial:ExtensionsでNoReverseMatchが出た時の対処法

Django Girls TutorialでDjango Girls Tutorail:ExtensionsのHomework: add more to your website!をやっていた時のこと。

Delete postという項目でpost_remove()という関数を作ってブログを削除するボタンを作りました。

でも、記事の削除はできるのですが、次のようなエラーになりブログのトップに戻りません。

NoReverseMatch at /post/24/remove/
Reverse for 'post_list' not found. 'post_list' is not a valid view function or pattern name.

実は、Django Girls Tutorial:ExtensionsはTutorial本体とはバージョンが違っていて、urls.pyの指定が間違っていました。blog/urls.pyのurlの指定のところで下のようにname='post_list'を付け加えないといけませんでした。

blog/urls.pyの変更前

urlpatterns = [
    url(r'^$', views.post_list),
]

blog/urls.pyの変更後

urlpatterns = [
    url(r'^$', views.post_list, name='post_list'),
]

views.pyファイルの中のredirect('post_list')で指定する値は、urlのnameの値になります。

nameに指定してあるから、redirectを呼んだ時urlがブログのトップページ'/'だとわかり、views.post_list関数が呼ばれます。

日本語版のTutorialをやったあとExtensionsをやっていて原因を見つけるのに時間がかかったので報告しておきます。

英語版をやっている人は問題ないです。

HerokuでGoogle Fontsを使うときはHTTPSが必要

HerokuでDjango Girls Tutorialを作ったときにGoogle Fontsを使ってみました。

ローカルではうまく表示されるのですが、Herokuへデプロイするとなぜかフォントが使われていません。

CSSでstyleを設定する前にGoogle Fontsを読み込まなければならないという情報もあり、stylesheetの読み込み順序を変えてみても、表示は変わりませんでした。なにより、ローカルで動いているのでソースコードが間違っているとは思いにくいです。結局、Herokuで使うときはHTTPSを使わないといけませんでした。

<link href="https://fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext" rel="stylesheet" type="text/css">

このように、httpsで始まるようにします。

Chromeの開発者ツールで見てみると、一番下のコンソールのところにhttpはHerokuで使えませんと出ていました。見落としていました。

Djangoで静的ファイルが見つからないときの探し方

Djangoを使っていて、CSSなどの静的ファイル(staticファイル)が見つからないときは次のようにするといいです。

css/blog.cssを探す場合、コンソールで次のように打ちます。

python manage.py findstatic --verbosity 2 css/blog.css

すると、次のように表示されます。

Found 'css/blog.css' here:
  /home/hoge/djangogirls/static/css/blog.css
Looking in the following locations:
  /home/hoge/djangogirls/static
  /home/hoge/djangogirls/myvenv/lib/python3.4/site-packages/django/contrib/admin/static

見つかるとFoundで表示してくれますし、見つからなくても探している場所を表示してくれますので確認しやすいと思います。

アプリケーションの場所のstaticディレクトリは、自動で探しにいくようです。

settings.pyのSTATICFILES_DIRでstaticファイルの場所を指定している場合など、場所の確認ができます。

Django Girls Tutorialでmigrateできない

Django Girls TutorialでHerokuへのデプロイをやったときのこと。

heroku run python manage.py migrate

は成功するのですが、

heroku run python manage.py createsuperuserがエラーになりました。

auth_nameが無いといわれます。no such tableだそうです。このようなエラーが出ます。

You have 14 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, blog, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

Traceback (most recent call last):
  File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/utils.py", line 65, in execute
    return self.cursor.execute(sql, params)
  File "/app/.heroku/python/lib/python3.6/site-packages/django/db/backends/sqlite3/base.py", line 328, in execute
    return Database.Cursor.execute(self, query, params)
sqlite3.OperationalError: no such table: auth_user

原因は、.gitignoreのlocal_settings.pyをsetingとミススペルしていたことでした。

Django Girls Tutorialでは、local_settings.pyがあればローカルのSqlite3を使い、herokuにはこのファイルをpushしないことでPostgreSQLを使うことにしています。

ところが、.gitignoreで指定できなかったため、herokuの方にlocal_settings.pyが転送されて、データベースとしてSqlite3が使われることになってしまいました。migrate時にエラーにならないのですが、データベースファイルができないらしく、tableがないというエラーになっていました。

リモートのファイルのみを消すには、

git rm --cached mysite/local_settings.py

git commit -m "Deleted remote local_settings.py."

git push heroku master

で消すことができました。

デプロイするだけでもいろいろトラブルになるんですね。

HerokuがWindowsで使えないときの対処法

PythonのDjangoに興味があり、Django Girls Tutorialというサイトを見てTutorialをやってみました。

Tutorialでは、Djangoの使い方の紹介とともに、Herokuでアプリを公開することもレッスンに含まれています。

そこで、はまったことを紹介します。

SSHキーの保存場所

heroku keys:add で作成される公開キーの保存場所は、C:/Users/(your name)/.ssh/id_rsa.pubです。MSYS2のMinGW 64-bitを使っていたのですが、MSYS2のホームディレクトリにある~/.ssh/のキーが使われませんでした。

Gitでheroku CLIのものを使う場合

heroku CLIというherokuコマンドを含むtoolがあるのですが、一緒にGitもインストールされます。最初MSYS2のgitをつかってgit push heroku masterしても、usernameとpasswordを聞かれてしまいすんなりとpushできません。

heroku loginでEmailとPasswordを聞かれてログインしているはずですが、認識していません。

結局heroku CLIでインストールされたGitを使うとusernameが聞かれなくなりました。

MSYS2のGitを使う場合は_netrcを.netrcにコピーする

heroku loginで認証したときのユーザー名やパスワードの情報は、ホームディレクトリの~/_netrcというファイルに保存されているようです。

MSYS2のGitを使えないか試してみて、ホームディレクトリの~/_netrc を~/.netrcにコピーすると使えることがわかりました。Heroku CLIのGitも使いたいときは_netをそのままにしておいた方がいいようです。

Pythonのバージョンとpsycopg2のバージョン

MSYS2のPythonは、3.4.5です。Django Girls Tutorialでは3.5.2を指定するよう書かれているので、runtime.txtに書いてgit pushしてみましたが、エラーで動きません。

エラーはpsycopg2をインストールするときに出て、ImportError: No module named 'six'となっています。

remote:            Traceback (most recent call last):
remote:              File "<string>", line 1, in <module>
remote:              File "/tmp/pip-build-799m2cq_/psycopg2/setup.py", line 583, in <module>
remote:                ext_modules=ext)
remote:              File "/app/.heroku/python/lib/python3.5/distutils/core.py", line 148, in setup
remote:                dist.run_commands()
remote:              File "/app/.heroku/python/lib/python3.5/distutils/dist.py", line 955, in run_commands
remote:                self.run_command(cmd)
remote:              File "/app/.heroku/python/lib/python3.5/distutils/dist.py", line 974, in run_command
remote:                cmd_obj.run()
remote:              File "/app/.heroku/python/lib/python3.5/site-packages/setuptools/command/install.py", line 61, in run
remote:                return orig.install.run(self)
remote:              File "/app/.heroku/python/lib/python3.5/distutils/command/install.py", line 551, in run
remote:                self.run_command(cmd_name)
remote:              File "/app/.heroku/python/lib/python3.5/distutils/cmd.py", line 313, in run_command
remote:                self.distribution.run_command(command)
remote:              File "/app/.heroku/python/lib/python3.5/distutils/dist.py", line 974, in run_command
remote:                cmd_obj.run()
remote:              File "/app/.heroku/python/lib/python3.5/site-packages/setuptools/command/install_scripts.py", line 17, in run
remote:                import setuptools.command.easy_install as ei
remote:              File "/app/.heroku/python/lib/python3.5/site-packages/setuptools/command/easy_install.py", line 49, in <module>
remote:                from setuptools.py27compat import rmtree_safe
remote:              File "/app/.heroku/python/lib/python3.5/site-packages/setuptools/py27compat.py", line 7, in <module>
remote:                import six
remote:            ImportError: No module named 'six'
remote:
remote:            ----------------------------------------
remote:          Rolling back uninstall of psycopg2
remote:        Command "/app/.heroku/python/bin/python -u -c "import setuptools, tokenize;__file__='/tmp/pip-build-799m2cq_/psycopg2/setup.py';f=getattr(tokenize, 'open', open)(__file__);code=f.read().replace('\r\n', '\n');f.close();exec(compile(code, __file__, 'exec'))" install --record /tmp/pip-dpg0rw8e-record/install-record.txt --single-version-externally-managed --compile" failed with error code 1 in /tmp/pip-build-799m2cq_/psycopg2/
remote:  !     Push rejected, failed to compile Python app.
remote:
remote:  !     Push failed

仕方がないので、sixをrequirements.txtに加えましたがエラーのままです。

runtime.txtの設定でpython-3.4.5をにしてみると、pipのインストールでエラーになります。No such file or directoryだそうです。
pythonのバージョンを下げるのは得策ではないようです。

remote: -----> Installing requirements with pip
remote:        /app/tmp/buildpacks/779a8bbfbbe7e1b715476c0b23fc63a2103b3e4131eda558669aba8fb5e6e05682419376144189b29beb5dee6d7626b4d3385edb0954bffea6c67d8cf622fd51/bin/steps/pip-install: line 7: /app/.heroku/python/bin/pip: No such file or directory
remote:  !     Push rejected, failed to compile Python app.
remote:
remote:  !     Push failed

pythonを3.6.1にしてもpsycopg2のエラーは変わりませんでした。

Django Girls Tutorialではpsycopg2のバージョンは、2.5.4を指定するようになっています。これを2.6や2.6.1にしてもエラーは変わりませんでした。

いろいろ試してみて結局psycopg2のバージョンを2.7.1にあげることでデプロイが成功しました。pythonのバージョンは、3.5.2と3.6.1のどちらでもよかったです。

requirements.txt

dj-database-url==0.4.2
Django==1.11
gunicorn==19.7.1
pytz==2017.2
whitenoise==3.3.0
psycopg2==2.7.1

Pythonを始めると、virutalenvやvenvといった仮想環境を使ってモジュールのバージョン管理をすることが推奨されていますが、バージョンが合わないとアプリケーションが動かなくなることが納得できました。

Herokuがうまく動かないときの情報が検索してもあまり出てこないので本当に困りました。