やる気がストロングZERO

やる気のストロングスタイル

PostgreSQLのデータ移行(dumpとリストア)

参考)
pg_dump

データのdump

pg_dump -h [hostname] -U [username] -c -t [tablename] -t [tablename] [dbname] > dump.sql

-h: ホスト指定
-U:ユーザー名指定。未指定だとbashのログインユーザー名になるっぽい
-c:テーブル削除文を追加
-a:データonly(-cと-aは同時に指定できない)
-t:テーブル指定。未指定だと全てのテーブルが対象になる

データのリストア

psql -U [username] [dbname] < dump.sql

DjangoのModelからデータを取り出す方法をまとめとく

Djangoのmodelからデータを取り出す方法をまとめとく
参考)
クエリを作成する | Django documentation | Django

※こっちのほうがいい感じにまとまっているかも
Django データベース操作 についてのまとめ - Qiita

データ構造

from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name

class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    n_comments = models.IntegerField()
    n_pingbacks = models.IntegerField()
    rating = models.IntegerField()

    def __str__(self):
        return self.headline

全てのオブジェクトを取得する

all_entries = Entry.objects.all()

オーダー条件をつける

# asc
all_entries = Entry.objects.all().order_by(“id”)
# desc
all_entries = Entry.objects.all().order_by(“-id”)
# 複数条件
all_entries = Entry.objects.all().order_by(“-id”, "view_order")

取得数制限(limit)

entries = Entry.objects.all().order_by(“id”)[0:10] # 先頭から10件取得
entries = Entry.objects.all().order_by(“id”)[5:15] # 6番目から10件取得
entries = Entry.objects.all().order_by(“id”)[0:10:2] # 先頭から10件取得を1件置きに取得する

フィルタを用いて特定のオブジェクトを取得する

プライマリーキー検索

entry = Entry.objects.get(pk=1)

フィールドルックアップ

filter()メソッドに[field__lookuptype=value]の形式で引数を渡す
lookuptypeはこちらを参考
QuerySet API reference | Django documentation | Django

entries = Entry.objects.filter(id__gte=2) # idが2以上
entries = Entry.objects.filter(id__exact=2) # idが2(id=2でも同じ結果)
entries = Entry.objects.filter(body_text__contain=“some_text") # body_textに"some_text”という文字列が含まれている

リレーションデータをフィールドルックアップの条件にする

filter()メソッドに[relationnamefieldlookuptype=value]の形式で引数を渡す。
[relationnamerelationnamefield__lookuptype=value]のように、いくらでも深くできる

entries = Entry.objects.filter(blog__name__exact='Beatles Blog’)

# 逆方向にたどる場合には、model名を小文字にしたものを使う
blogs = Blog.objects.filter(entry__headline__contains='Lennon’)

モデルの値を使ったフィルタ(F()オブジェクト)

比較条件にそのmodelの値を使いたい場合、F()オブジェクトにフィールド名を与えると参照できる。

from django.db.models import F
entries = Entry.objects.filter(n_comments__gt=F('n_pingbacks’))

entries = Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2) # 数値計算もできる

entries = Entry.objects.filter(authors__name=F('blog__name’)) # 関連モデルのフィールドも参照できる

from datetime import timedelta
entries = Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3)) # 日付計算もできる

F('somefield').bitand(16) # bit演算もできるとのこと

文字列の結合はできなかった

entries = Entry.objects.filter(authors__name=F('blog__name’) + “suffix") # エラー

下記のようにする。
参考) python - Can I use Django F() objects with string concatenation? - Stack Overflow

from django.db.models.functions import Concat
from django.db.models import Value
Entry.objects.filter(authors__name=Concat(F("blog__name"), Value("suffix"))) # これでいけた。

Qオブジェクトを使ったAND,OR条件指定

# and条件
entries = Entry.objects.filter(Q(id=1) & Q(n_comments=1)) # idが1かつn_commentsが1のもの

# or条件
entries = Entry.objects.filter(Q(id=1) | Q(n_comments=1)) # idが1もしくはn_commentsが1のもの

# not条件
entries = Entry.objects.filter(~Q(id=1)) # idが1ではないもの

Django カスタムカラムを含んだ多対多モデルの書き方

Djangoだと多対多の中間テーブルを自動的に作成してくれるが、この中間テーブルになにかデータをもたせたい場合がある。
その場合は以下のような定義をしないといけない。

モデル定義

class Person(models.Model):
    name = models.CharField(max_length=128)

    def __str__(self):
        return self.name


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(Person, through='Membership')

    def __str__(self):
        return self.name


class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)

データ登録

ringo = Person.objects.create(name="Ringo Starr") #Person:ringoがDB上にも作成される
paul = Person.objects.create(name="Paul McCartney") #Person:paulがDB上にも作成される
beatles = Group.objects.create(name="The Beatles”) #Group:beatlesがDB上にも作成される

m1 = Membership(person=ringo, group=beatles, date_joined=date(1962, 8, 16), invite_reason="Needed a new drummer.") #ringoとbeatlesの関連が作成される(DBにはまだ作成されない)
m1.save() # この時点でDB上にも作成される

Membership.objects.create(person=paul, group=beatles, date_joined=date(1960, 8, 1), invite_reason="Wanted to form a band.") #paulとbeatlesの関連がDB上にも作成される

データ表示

beatles.members.all()

データ削除

beatles.members.remove(ringo)

Djangoのログ設定

デフォルトのLogging設定だと 実行時エラーが起こった時に、

Debug=Trueの場合: ブラウザ上にエラーメッセージとスタックトレースが表示される。

Debug=Falseの場合: ブラウザ上にはnginxのstatusコードのみ。

となっていて、本番環境でエラーが起こった場合の原因を探りにくい。

adminとしてメールアドレスを設定しておくと、そこにメールが飛ぶようにデフォルト設定されているらしいが、 ファイルに出力したいと思ったので下記設定を行った。

※ec2上Docker運用でこのログファイルをec2<->コンテナで同期させた場合、パーミッションがrootになってしまいDjangoからログが書き込まれなかった。要調査。
↑調査したところ、ちゃんと書き込まれていた。構文エラーのときにはロガー自体が動作しないのでログに書き込まれないという状態だった。
パーミッションがrootになってしまうのは、コンテナ内でuwsgiをroot権限で動かしていたから。本当ならちゃんとuserを作成したほうが良さげ。

# logging_setting。デフォルト設定に対して、ファイル出力するように設定を追加し、時間も記録できるようにフォーマットを追加したもの
LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'filters': {
        'require_debug_false': {
            '()': 'django.utils.log.RequireDebugFalse',
        },
        'require_debug_true': {
            '()': 'django.utils.log.RequireDebugTrue',
        },
    },
    'formatters': {
        'verbose': {
            'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
            'style': '{',
        },
        'simple': {
            'format': '{levelname} {message}',
            'style': '{',
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'filters': ['require_debug_true'],
            'class': 'logging.StreamHandler',
        },
        'mail_admins': {
            'level': 'ERROR',
            'filters': ['require_debug_false'],
            'class': 'django.utils.log.AdminEmailHandler'
        },
        'file': {
            'level': 'INFO',
            'class': 'logging.FileHandler',
            'filename': '/logs/django.log', # ログファイルのパスを指定
            'formatter': 'verbose',
        },
    },
    'loggers': {
        'django': {
            'handlers': ['console', 'mail_admins', 'file'],
            'level': 'INFO',
        },
        'py.warnings': {
            'handlers': ['console', 'file'],
        },
    }
}

2018年活動まとめ

2017年末、2018年はアウトプットを増やそうと決めた。
主にはブログによるアウトプットになったが一応1年以上継続できている。
ブログの内容を元に、今年一年行ってきたことを振り返ってみる。

英語

「どんどん話すための瞬間英作文トレーニング」がよかった - やる気がストロングZERO

IKnowで単語量をひたすら増やしている。
フレームワークのオフィシャルドキュメントは結構読めるレベルにはなった。
ただしそこそこ負荷を感じるので日本語があるならそちらを読みたくなるというレベル。

ヒアリング、スピーキングのレベルは全然あがらなかった。
英会話教室やDMM英会話もやってみたが、スケジュールが安定せずモチベーション維持が困難だった。
実践回数が少なすぎる。

Docker

Nginx -> uWSGI -> Djangoで動作するDocker Imageを作った - やる気がストロングZERO
Dockerを使うと何がいいのか?悪いのか? - やる気がストロングZERO

Dockerの便利さを理解し、開発環境ではある程度使いこなせるようになったと思う。
本番環境運用でのノウハウを得ていきたい。

DB

テーブル設計:ある項目によって保存したいデータ内容が変化する場合 - やる気がストロングZERO
SQLアンチパターン:EAV(エンティティ・アトリビュート・バリュー) - やる気がストロングZERO
トランザクション内で「重複チェック」してから「DB登録実行」しても重複が発生する場合がある - やる気がストロングZERO
トランザクション分離レベル Serializableでも同時実行でエラーがでる可能性がある - やる気がストロングZERO

本を数冊読んだ。
・達人に学ぶDB設計 徹底指南書
SQLアンチパターン

理想形のテーブル設計の知識はある程度得られていると思う。

今後、より得ていきたい知識は、
「同時実行を意識した設計のベストプラクティス」
「既存のひどいテーブル設計に対してどうアプローチしていくのか」
という部分。

Python

Phthonの内包表記まとめ - やる気がストロングZERO
pythonパッケージをターミナルから実行する場合は[-m]オプションを使う - やる気がストロングZERO
pipenv [install, update, sync] [Pipfile, Pipfile.lock]について - やる気がストロングZERO
python3のdatetimeとtimezoneとpytzについて - やる気がストロングZERO

unittestを使ったテスト駆動開発を含めて、ある程度アプリケーションを作れるようになった。

まだまだ知らない言語仕様もあるが、必要に迫られるまではとりあえずこれで良いかなという感じ。

Django

uWSGIの使い方(Djangoと使う場合) - やる気がストロングZERO
herokuでdjangoアプリをデプロイする - やる気がストロングZERO
django 複数のformが存在するページを作りたい[Formsetsを使う] - やる気がストロングZERO

Djangoで一つサービスを作った。

現在もう一つサービスを作成中なので、ここでもっと理解度を上げていきたい。

開発手法

コードレビュー出すときに意識すること - やる気がストロングZERO
競技プログラミングでもしっかりとTDDを行うと良い - やる気がストロングZERO
初めてテスト駆動開発を行ってみて感じたこと - やる気がストロングZERO
工数見積のテクニックまとめ - やる気がストロングZERO
「テスト駆動開発」を読んでやっとテスト駆動開発ができるようになった - やる気がストロングZERO
プログラミング作業での品質の上げ方 - やる気がストロングZERO
「アジャイルプラクティス 達人プログラマに学ぶ現場開発者の習慣」が良かった - やる気がストロングZERO
個人開発合宿やってみた - やる気がストロングZERO

アジャイル開発的な手法とテスト駆動開発の知識と実践を出来たのが大きい。

アジャイル開発の考え方を身に着けて、 ずっと不安として抱えていた見積もり系の問題に一応指針を持つことができた。
(とはいえ問題の起こりやすい部分なのは変わらないが。。)

テスト駆動開発の手法を身に着けてシンプルな疎結合な実装をできるようになったし、実装が迷走することも無くなった。

AWS

AWS EC2でインスタンス作成する際に理解しときたい周辺設定 - やる気がストロングZERO

AWSは無料枠で一通りの環境構築を行ってみた。
ただし実際に何かを運用してみないとわからないことが多いので、今開発中のサービスを早く運用に乗せるようにする。

シェル

awkまとめ - やる気がストロングZERO
簡単な自動化はシェルコマンドで手軽にできる - やる気がストロングZERO

使いこなせると便利なのだが、たまに必要になると書き方を忘れている。

思い出したときにブログにまとめるようにする。

来年は?

より運用方向での知識を得ていきたい。

ブログアウトプットについて

ブログに残しておくと何やってたのかが残るのが良い。
知識として定着もしやすい気がする。

また、人に「どういうエンジニアなのか?」を伝える手段としても有効であるのを最近感じている。

PyCharmでDocker側のpythonインタプリタを使ってデバッグ実行する設定

サンプル用コードを用意

git clone https://github.com/mixmaru/django_nginx_uwsgi_environment.git

PyCharmで開く。

インタプリタの設定

Preferences->Project Interpreter画面の
Project Interpreter項目でaddを押す
開いた画面の左側メニューから「Docker Compose」を選択し,

Server: Docker  
Configuration file(s) ./docker-compose.yml  
Service: app  
Environment variables: (空欄)  
Python interpreter path: python3 # pythonと初期入力されているがpython3と入力(サンプルコードのappのコンテナ環境ではpython3としてインストールしているため)  

OKを押す。

PyCharmのDjangoサポートを有効にする

Preferences->Languages & Frameworks -> Django画面で

・Enale Django Supportのチェックボックスにチェックを入れる  
・Django project root:/(your_dir_path)/django_nginx_uwsgi_environment/app/sync/project  
・Settings:project/settings.py  
・Manage script:manage.py(自動的に入った)  

OKを押す。

Run/Debug Configurationsを設定する

デバッグ実行ボタン横の[Add Configuration…]と表示されている部分をクリック
Run/Debug Configurations画面が開く。
+ボタンを押してDjango serverを選択
Configurationタブにて

・Host:0.0.0.0  
・Port:80  

あとは初期値のままでOKを押す

Djangoの実行

PyCharm左上の実行ボタン「緑三角」を押す。
なぜかdb接続がうまくいかないことがあるが、(dbコンテナよりも先にappコンテナが立ち上がって見つけられないっぽい) 再度実行したりを何度かしているとうまくいく。(docker-compose.ymlのdepends_on設定は見ていない?)

http://0.0.0.0/でアクセスするとDjangoの表示が見える。
(サンプルコードでは「ALLOWED_HOSTSに0.0.0.0を設定していない」的なエラー画面が表示された)

これで、ブレークポイントを設置してデバッグ実行でラインデバッグできる。

テーブル設計:ある項目によって保存したいデータ内容が変化する場合

SQLアンチパターン:EAV(エンティティ・アトリビュート・バリュー) - やる気がストロングZERO ↑ここで書いた事と同内容。

例えばペットの種類によって保存したい内容が異なる場合

例)
犬:名前、鳴き声、リードの色、小屋の色
猫:名前、鳴き声、毛色、しっぽの形

こうしたい

ペットテーブル
・ペットID
・名前
・鳴き声
・ペットタイプ(犬 or 猫)

ペット犬テーブル
・ペットID
・リードの色
・小屋の色

ペット猫テーブル
・ペットID
・毛色
・しっぽの形

こんな感じがいいのかなと思っている。

こうしたくない

既存実装で以下のような感じのテーブルをよく見る。

ペットテーブル
・ペットID
・名前
・鳴き声
・ペットタイプ(犬 or 猫)
・リードの色
・小屋の色
・毛色
・しっぽの形

ペットタイプが「犬」の場合にも毛色とかしっぽの形にデフォルト値が入っていたりして、DBデータだけみるとよくわからない感じになっている。