TechnologyMemo

Django メモ

Model.objectsなどを参照の際のvscodeのエラー対策

djangoが動的に作成したプロパティを参照すると、vscodeがエラーをはいて鬱陶しい。 django用のpylintを用いるようにする

pip install pylint-django

vscodeの設定

"python.linting.pylintArgs": [
 "--load-plugins=pylint_django"
],

参考 https://stackoverflow.com/questions/45135263/class-has-no-objects-member

Fieldクラスの第一引数

フィールドクラスの第一引数には、そのフィールドの人間用の名前をつけることができる。 管理サイトでの表示に使われたりするそうだ

参考 https://docs.djangoproject.com/ja/2.0/topics/db/models/#verbose-field-names

複数のDBを扱いたい場合の例

参考 https://tokibito.hatenablog.com/entry/20160202/1454344534

複合ユニーク制約を付与したい

以下のようにする(models.py):

class Exsample(models.Model):
    sample_1   = models.IntegerField()
    sample_2   = models.IntegerField()

    class Meta:
        unique_together=(("sample_1","sample_2"))

参考 http://blog.livedoor.jp/extension/archives/50251791.html

画像のアップロード

参考 https://qiita.com/narupo/items/e3dbdd5d030952d10661

マイグレーション

1.settings.pyを編集:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'myapp', #任意のアプリケーションを追加
)

2.models.pyを編集

3.makemigrationsにて、モデルの変更を通知:

$ ./manage.py makemigrations
Migrations for 'myapp':
0001_initial.py:
...

4.実行されるddlを確認(ここで0001はmakemigrationsにて出力された値を指定):

$ ./manage.py sqlmigrate myapp 0001

5.マイグレーションの実行:

./manage.py migrate

2回目以降のマイグレーションも2.から同じ処理を繰り返す。 また、4.のddlの確認、5.のマイグレーションの実行では1つのDBのみが対象(指定なしの場合はdefaultのDBが対象) になるため、以下のコマンドでマイグレーションするDBを指定する必要がある

./manage.py migrate --database=exsample

makemigrationsで変更が検知できない場合、アプリ名を明示してやると上手くいく

python manage.py makemigrations exsample_app

参考 https://remotestance.com/blog/2612/

参考 https://docs.djangoproject.com/en/2.0/topics/db/multi-db/#synchronizing-your-databases

FileFieldを持つモデルが削除されたら、対応するファイルを削除する

削除のタイミングで発生するシグナルを拾ってファイルを削除する

参考 https://stackoverflow.com/questions/16041232/django-delete-filefield

画像のサムネイルを作成したい

django-imagekitが使える。 例

from django.db import models
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFill

class Profile(models.Model):
    avatar = models.ImageField(upload_to='avatars')
    avatar_thumbnail = ImageSpecField(source='avatar',
                                    processors=[ResizeToFill(100, 50)],
                                    format='JPEG',
                                    options={'quality': 60})

このavatar_thumbnailのURLにリクエストを送ると、ImageKitが引数に対応した処理を行い、 サムネイルを返す(引数次第では別の画像処理も可能)。 また一度画像にリクエストを送ると、次以降はMEDIA_ROOT/CACHE下のファイルを参照して画像を返すようになる。

参考 https://github.com/matthewwithanm/django-imagekit

例のprocessorsに使えるものは以下のページを参照

http://pilkit.readthedocs.io/en/latest/#processors

自分でも簡単に定義できそう。

QuerySetにて発行されるSQLを確認したい

QuerySetのqueryを確認する。 shellを立ち上げて確認するのが手っ取り早い。 例:

python manage.py shell
>>> from polls.models import Question
>>> print(Question.objects.all().query)

参考 https://qiita.com/junjis0203/items/11e08cc177f32be0aae4

DBの列に関数を適用した上でDistinctしたい

selectする列などは、django.db.models.functionsにて関数を適用できる。 関数を適用した列に別名をつけて、distinctしてやればよい。

from django.db.models.functions import ExtractYear, ExtractMonth
a = Picture.objects.values(year=ExtractYear("take_date"),
                           month=ExtractMonth("take_date")).distinct()

https://docs.djangoproject.com/en/2.0/ref/models/database-functions/

テンプレート内で文字をフォーマットしたい

stringformatフィルタを用いる。古い形式のPythonのフォーマットしかできないっぽい

参考 https://docs.djangoproject.com/ja/2.0/ref/templates/builtins/#stringformat

管理サイトで画像を表示したい

以下のサイトの二番目に回答がついている、ver1.9対象のものが役に立つ

参考 https://stackoverflow.com/questions/16307307/django-admin-show-image-from-imagefield?noredirect=1&lq=1

また、画像のHTMLを返す関数はモデルではなく、admin.pyの方に以下のように定義してもよい:

class PictureAdmin(admin.ModelAdmin):
    list_display = ('admin_put_picture',) #一覧に表示される項目
    readonly_fields = ('admin_put_picture',)

    def admin_put_picture(self, row):
        return mark_safe('<img src="{}" style="width:100px;height:auto;">'.format(row.picture_thumbnail.url))

admin.site.register(models.Picture, PictureAdmin)

settings.pyのALLOWED_HOSTS

Hostヘッダインジェクションを防ぐために、HTTPのHostヘッダに許される値を指定する。 ドメインそのまま書けばおk。

バージョンによっては、デバッグフラグがTrueの時はALLOWED_HOSTSが無視されたりもしたらしいが、Django2.0ではデバッグフラグ関係ない?

参考 https://docs.djangoproject.com/ja/2.0/ref/settings/#allowed-hosts

EC2, Apache上での環境構築

以下のサイトが非常に参考になった。

前提

以下の環境にて作業を実施

  • 2018/07に実施
  • Amazon Linux 2 AMI - ami-e99f4896を使用
  • Apache2.4はインストール済
  • その他は何もインストールしていない

手順

python3のインストール

sudo yum install python3

python3系が入る。
pip3も入る。
デフォルトのpythonはそのまま。
pipからpip3をアップデートやインストールしたり、pip3からpipをアップデートやインストールするのはやめた方がいいらしい。

python仮想環境の準備

$ pwd
/var/www/environments

$ sudo python3 -m venv myenv

$ sudo su
# source myenv/bin/activate

以下は仮想環境上で実行。 仮想環境上では、python3, pip3がデフォルトとなっており、python, pipコマンドで3系に対する操作が可能。

django, mod_wsgiのインストール

yumでmod_wsgiをインストールすると、python2用のものが入るので、pip3を用いた方がよいみたい。
仮想環境上で以下のコマンドでインストールする。
# pip install django
# pip install mod_wsgi
以下のようにapxsが足りないと怒られたので、httpd-develをインストールする。
RuntimeError: The 'apxs' command appears not to be installed or is not executable. Please check the list of prerequisites in the documentation for this package and install any missing Apache httpd server packages.

# yum install httpd-devel



また、gccがないと動かせないので、以下で開発用ツール一式インストールする
# yum groupinstall "Development Tools"


python3-develが入っていないことが原因で、以下のエラーが発生。

src/server/wsgi_python.h:24:10: fatal error: Python.h: No such file or directory
#include <Python.h>

以下のコマンドでインストールする

# yum install python3-devel

参考 https://sekisuiseien.com/computer/10626/

その後再度mod_wsgiをインストールした。

Apacheの設定

/etc/httpd/conf.modules.d/mod_wsgi.confに、以下のwsgi_moduleをロードする設定を記述

LoadModule wsgi_module /var/www/environments/myenv/lib/python3.7/site-packages/mod_wsgi/server/mod_wsgi-py37.cpython-37m-x86_64-linux-gnu.so

/etc/httpd/conf.d/django.confに以下の設定を記述

WSGIPythonHome /var/www/environments/myenv
WSGIScriptAlias /app /var/www/environments/mysite/mysite/wsgi.py
WSGIPythonPath /var/www/environments/mysite:/var/www/environments/myenv/lib/python3.7/site-packages

<Directory /var/www/environments/mysite/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>

apacheを起動し、ドメイン名/appにアクセスしてdjangoの起動を確認出来たら完了。

静的ファイル周りの設定

STATIC_ROOTは、collectstaticを実行したときに静的ファイルを集めるパス。

MEDIA_ROOTは、ユーザーがアップロードするファイルのパス。

STATIC_URLは静的ファイルのURL。
プロジェクトのURL + STATIC_URLといった感じでアクセスする。
テンプレート内にて、{% static "myapp/image.jpg" %} といった感じでアクセスできる。
本番環境では、Webサーバの設定にてSTATIC_URLがSTATIC_ROOTのディレクトリを参照するよう
設定する必要がある。
開発環境(DEBUT=True)の場合は、
Djangoが各アプリのstaticディレクトリ下からファイルを取ってくるので特別な設定は必要ない。

MEDIA_URLは、ユーザがアップロードしたファイルにアクセスする際のURL。
プロジェクトのURL + MEDIA_URLといった感じでアクセスする。
FileFieldのインスタンスに対して、 sample_record.sample_file.url()
にてURLを取得できる。
クライアントからアップロードしたファイルを参照したい場合、
本番環境では、MEDIA_URLにてMEDIA_ROOTを参照できるようWebサーバの設定が必要。
開発の場合でもstaticfileと違いDjangoがよしなにやってくれないので設定が必要。
DEBUG = Trueの時のみurls.confに
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
といった設定を追加する必要がある。

ここらへん鑑みて、設定としては以下のようにしている。

settings.py

if DEBUG:
    MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
else:
# Webサーバの設定にて、http://ドメイン/static にアクセスした際はSTATIC_ROOTを、
# http://ドメイン/media にアクセスした際はMEDIA_ROOTのファイルを参照するよう設定。
    STATIC_ROOT = '/静的ファイルを配置したい絶対パス/'
    MEDIA_ROOT = '/メディアファイルを配置したい絶対パス/'

STATIC_URL = '/static/'
MEDIA_URL = '/media/'

urls.conf

urlpatterns = [
    ...
]

# 開発の場合のみ、djangoにてメディアファイルを配信するよう設定
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

参考 https://qiita.com/okoppe8/items/38688fa9259f261c9440

mod_wsgiのWSGIScriptAlias

最後に/付けるのはまずいみたい。しばらくはまった。

ダメな例 WSGIScriptAlias /hoge/app/ /var/www/environments/mysite/mysite/wsgi.py

問題ない例 WSGIScriptAlias /hoge/app /var/www/environments/mysite/mysite/wsgi.py

これ WSGIScriptAlias / /var/www/environments/mysite/mysite/wsgi.py が通るから 最後にスラッシュついてても問題ないかと勘違いしてしまった。