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がうまく動かないときの情報が検索してもあまり出てこないので本当に困りました。

VMware Workstaion Playerでマウスのキャプチャの設定

VMware Workstation Playerで、ゲストOSでマウスやキーボードを使えるようにすることをキャプチャとがグラブ(grub、捕まえる)というようです。

自分はAlt-Tabでウインドウを切り替えることが多いです。VMware Playerの場合、一度ウィンドウをクリックしてグラブされてしまうと、Alt-TabがLinuxのゲスト内でのウィンドウの切り替えになってしまい、マウスで外のウィンドウをクリックして変えていました。

キーボードだとCtrl-Altの同時押しでリリースする設定になっているのですが、どうにも使いずらいです。そこで、C:\Users\(your name)\AppData\Roaming\VMware\preferences.iniに次のように設定してAlt-Shiftで変えられるようにしました。

preferences.ini

pref.hotkey.shift = "TRUE"
pref.hotkey.control = "FALSE"
pref.hotkey.alt = "TRUE"
pref.hotkey.gui = "FALSE"

マウスのリリースは上記で設定できるのですが、今度はAlt-TabでVMware Playerの画面を最前面に持ってきたとき、マウスがその画面内に入ってないとキーボードの入力を受け付けてくれません。

次のように設定すると、マウスカーソルがVMware Playerの画面内になくてもキーボードを打ち始めると入力できるようになります。

preferences.ini

pref.grabOnKeyPress = "TRUE"

以上の設定をすると、Alt-Shiftでマウスのキャプチャを解除できます。また、Alt-TabでVMware Workstation Playerを前面にもってきたときはそのままキーを押すことでマウスをキャプチャできるようになります。ホストOSとゲストOSを行き来するときに、いちいちマウスを操作するのは効率が悪いのでちょっとした設定ですが便利です。

今回はこちらの情報を参考にさせてもらいました。ありがとうございます。

[IT] VMware のホストへ戻るキーの設定
How to set input grab for VMWare Workstation Player 12

VirtualBoxの共有フォルダの自動マウント

VirtualBoxの共有フォルダを自動マウントしようとしたところ、以前とは少し違っていたので紹介します。

まず、VirtualBoxのメニューのデバイス、共有フォルダで設定をします。名前をshareとしました。自動マウントと永続化のチェックは入れておきます。

次に、Ubuntu 16.04 x64で手動でマウントするには以下のようにします。

$ mkdir sharepoint
$ sudo mount -t vboxsf -ouid=hoge,gid=hoge share sharepoint

これを、Ubuntuの起動時に自動でマウントするには/etc/fstabに次のようなエントリーを加えます。
名前がhogeさんの場合です。

/etc/fstab

share	/home/hoge/sharepoint	vboxsf uid=hoge,gid=hoge,comment=systemd.automount	0	0

fstabの各項目の間はtabを使うといいです。

そして、/etc/modulesに次の項目を加えます。

/etc/modules

vboxsf

ここで、手動でマウントできるか確かめてみます。

$ sudo mount share

マウントできれば/etc/fstabの設定は大丈夫です。あとは再起動して自動でマウントできるか確かめます。/etc/modulesの設定を忘れるとUbuntuの起動が途中で止まってしまいます。

以前作ったときは、/etc/fstabにcomment=systemd.automountという設定は必要なかった気がします。systemdではこれがないといけないようです。

VirtualBoxのGuestAdditionsのサービスが起動してからでないとマウントできないからというのが理由だそうです。

Vagrantだと、Vagrantfile内でsynced_folderを設定すると自動でやってくれます。その辺が少し違っていますね。

参考にしたサイト
Virtualbox shared folder mount from fstab fails; works once bootup is complete
VirtualBoxの共有フォルダの自動マウント化

なお、共有フォルダの設定で自動マウントにすると/media/sf_(共有フォルダ名)でマウントされています。でも、所有者がrootグループがvboxsfになっていて使いにくいのですね。それで自分でマウントすることにしました。

そのあたりのことはこちらを参照ください。

VirtualBoxにおいて、ホストOSとゲストOS (CentOS) 間の共有フォルダを作成する方法

追記(2017/06/30)

共有フォルダの自動マウントで設定された/media/sf_(共有フォルダ名)は、vboxsfというグループに自分のユーザー名を加えると簡単に読み書き出ることがわかりました。

sudo gpasswd -a (ユーザー名) vboxsf

こうすれば、わざわざ自分でマウントする必要はなかったですかね。

Visual Studio Codeのインテリセンスで入力候補を選択するショートカットを無効にする方法

Visual Studio Codeを使い始めました。

インテリセンスが効いて、コードを入力するのが楽になりました。

ただ、1つ気になったのは、入力候補が出た時にエンターキーを押すとどんな場合にも入力候補が選択されて確定してしまうこと。

その入力候補を選びたくないときにはどうすればいいのか結構調べました。

その結果、3つの方法を考えました。

設定を変えない方法

行を変えたいときはCtrl+Enter。

それ以外の時は、スペースキーなどEnterキー以外のキー入力で無視できます。

この方法は、設定は変えませんが慣れるまで時間がかかりますね。

Enterキーによる確定を無効にする方法

Enterキーでは入力候補の確定をしないでTabキーのみで確定にする方法です。次のように設定を変えます。既定で"enter"に設定されているエントリーをkeybindings.jsonに入力するとエンターキーによる確定が無効になります。ここで注意したいのが、2つめの要素でacceptSelectedSuggestionOnEnterの前にマイナスがついていること。マイナスがつくことで既定のキーバインドが無効にされています。Tabで決定する設定は既定に入っていますので設定する必要はありません。

[
    {
        "key": "enter",
        "command": "-acceptSelectedSuggestionOnEnter",
        "when": "acceptSuggestionOnEnter && editorTextFocus && suggestWidgetVisible && suggestionMakesTextEdit"
    }
]

この方法は、確かにエンターキーでの確定はしなくなりますが、Tabキーを多く押す必要があってちょっと左手の小指の負担が多くなることが玉にきずです。

入力候補の確定をShift+Enterにする方法

変換候補の確定をShift+Enterキーにする方法。次のような設定をkeybindings.jsonに加えます。

[
    {
        "key": "shift+enter",
        "command": "acceptSelectedSuggestionOnEnter",
        "when": "acceptSuggestionOnEnter && editorTextFocus && suggestWidgetVisible && suggestionMakesTextEdit"
    },
    {
        "key": "enter",
        "command": "-acceptSelectedSuggestionOnEnter",
        "when": "acceptSuggestionOnEnter && editorTextFocus && suggestWidgetVisible && suggestionMakesTextEdit"
    }
]

これは、次のように作りました。

Visual Studio Code でファイル―>基本設定―>キーボードショートカットと進みます。

キーボードショートカットの画面で、検索窓にenterと入力してエンターキーを押します。

すると、Enterキーに関するショートカットの一覧が出ますので、マウスをacceptedSelectedSuggestionOnEnterの上にのせて、左にあるペンマークをクリックします。

任意のキーを押してくださいと出ますので、ShiftキーとEnterキーを同時に押して、もう一度エンターキーを押します。

すると、キーバインドがshift+enterになり、ソースがユーザーとなって完成です。設定された内容は、上部にあるkeybindings.jsonをクリックするとみることができます。

まとめ

Visual Studio Codeのショートカットを無効にする方法を紹介しました。実際に使ってみて、自分の場合は設定を変えない方法がいいかなと思っています。どれも一長一短がありますのでより自分になじむ方法を選んでください。

Vagrant用のUbuntu 16.04 x64のBoxを作る

Vagrant用のUbuntu 16.04 x64のBoxを作りました。

こちらのサイトが参考になりました。ありがとうございます。

Ubuntu 16.04のVagrantで使うboxを作成する

何かあったときにXサーバーがあるといいので、Desktop版で作りました。容量は大きくなってしまいますけれど。

1つだけ注意があって、インストール時にvagrantユーザーを作ってしまえばスーパーユーザーになれます。しかし、別のアカウントを作って後からvagrantユーザーを作るときは、パスワードの設定とスーパーユーザーになれる権限を忘れずにしなければいけません。

$ sudo adduser vagrant
$ sudo passwd vagrant
$ sudo gpasswd -a vagrant sudo

この3つを忘れずにしないと、なんか動かないことになってしまいます。adduserはuseraddにするとホームディレクトリがつくられません。オプションuseradd -mが必要です。パスワードの設定を忘れると、ログインできません。sudoをできるようにしないと、Vagrantの設定ができないというエラーになります。

パスワードなしでスーパーユーザーにならないとネットワークの設定ができないと言われ、それってセキュリティ的にどうなの?と思ってしまいました。

作ってみて、容量が大きいと時間がかかって、エラーで何度も繰り返すのが大変でした。

Vagrantでprivate_networkを指定したときの仕組み

Vagrantでネットワークの設定は3つあります。このうち、private_networkを指定したときにVagrantは少し複雑な処理をしていました。

1つめのVagrantでpublic_networkを指定したときは、VirtualBoxのブリッジアダプターが使われ、外部のルーターなどからDHCPでIPアドレスをもらいます。

2つめのforwarded_portを指定したときは、VirtualBoxのNATが使われて、VirtualBox内蔵のネットワークからDHCPでIPアドレスをもらいます。

3つめのprivate_networkを指定したときは、VirtualBoxのホストオンリーアダプターという仮想ネットワークアダプターが使われます。この場合、上の2つとは少し違っていて、DHCPサーバーはデフォルトでは設定されていません。このことは、ホストの仮想ネットワークアダプターのプロパティからわかります。IPアドレスは、192.168.33.1というアドレスが設定されています。

このため、ゲストOSは192.168.33.x(xは2から254)というネットワークを自分で設定しないといけません。Vagrantではvagrant upのときに、ssh接続を使って/etc/network/interfacesに次のようなエントリーを書き込みます。

#VAGRANT-BEGIN
# The contents below are automatically generated by Vagrant. Do not modify.
auto enp0s8
iface enp0s8 inet static
      address 192.168.33.10
      netmask 255.255.255.0
#VAGRANT-END

これでIPアドレスが設定されて、ホストとゲストで通信ができるようになります。このような仕組みのため、vagrant upのときにvagrantというユーザーでSSHで接続できることと、ファイルに書き込む権限がいるためにスーパーユーザーになれることが条件になります。

Vagrantを使わずVirtualBoxのみでプライベートネットワークを組んだ時は、手作業でネットワークインターフェース、この場合はenp0s8の設定をする必要があります。

自分でvagrantboxを作ろうとして、わかったことでした。