@kotyのブログ

PythonとかAWSとか勉強会のこととかを、田舎者SEがつづります。記事のライセンスは"CC BY"でお願いします。

謹賀新年2019

あけました。

昨年の振り返り

おかげさまで家族みんなが大きな病気もせず、一年を過ごせました。それだけで他に言うこともないのですが、強いて振り返ります。

子供中心の毎日

相変わらず子供中心の日々です。子供のお尻を拭くためにunkが出るのを待つような時間も含め日々家事や育児の一端を担えてとても充実しています。長時間勤務が常態化した職場だとこうはいきません。

今が一番かわいいと昨年は言いましたが、その時期は過ぎて小憎たらしさも出てきました。胆力、我慢強さが試されています。

転職4年目だった

メインはVue.js、Firebase案件でした。技術的なことだけでなくサービス運営やスクラムなど大変勉強になっています。この件は良いのですがうまくいかない件もありまして、やはりおひとり様案件は良くないというのが反省です。属人化してヘルプが難しくなります。

それとですが、

これは何となく感じるところはあります。どこの会社さんも人が足りないと言っていてソフトウェアエンジニアはどこに行っちまったんだろうと思うわけですが、おそらくジギョーカンパニーに行ってるんでしょう。大変良い流れだと思います。

input/output

相変わらず本はほとんど読みませんでした。4,5冊くらいだろうか。。。読書会があったのは良かったです。k8sとデータ分析をやりたいと昨年は言ったところちょっとだけ始めました。素振りしたとも言えないくらいです。

ブログ記事は、発信を強化するという会社の方針にも推されて、前年までよりは割と書きました。

ポートフォリオはまったく増えませんでした。githubにコミットした日なんて数えたら10日しかありませんでした。。。

毎朝15分の英語の勉強を継続できたのは良かったです。たった15分でも毎日やるとけっこう違います。この1年半でTOEICスコアが665→710→775と誤差ではなく明確に向上し、多少英語が聞こえてくるようにはなりました。一番の課題はスピーキングかなーと思います。まったく口をついて出てきません。

水泳関係

OWSの大会にひとつ出ました。引き続きぼちぼち練習は継続します。

今年の抱負

不惑も過ぎましたが不惑には程遠いです。それどころか周りも惑わせていきます。

input/output

いろいろ手を広げてもできないので、「ゼロから作るDeep Learning」をしっかり勉強したいです。

英語は当面は今のラジオ英会話を継続。結果としてTOEIC800点台をとれればいいな。オンライン英会話もやってみたいですが。。。

水泳関係

たまには400とか800mの練習もしたいところ、1分半/100m巡航ができなくなり本数を数えづらくなった関係で最近は200mの練習ばかりしています。このあたりをサポートするアプリを作りたい。

ことしもよろしくお願いします

健康第一で。

第8回「体系的に学ぶ 安全なWebアプリケーションの作り方 第2版 読書会 参加記録

nseg.connpass.com

13日の予定が参加者ひとりのため延期になっていた。参加者は3名だった。

トピックとしては、「メール送信の問題」と「ファイルアクセスにまつわる問題」。

メール送信の問題

ヘッダーに不正な改行コードを入れられたときに発生する。

不勉強なもので今まで意識したことがなかった。 今どきは自前のメールサーバーは持たず、SendGridやAWSのSESを始めとするマネージドサービスを使うのが自分の周りでは普通になっている。その際送信時のプロトコルSMTPではなくweb apiを呼んでマネージドサービスの向こう側にSMTPがある。

Djangoだと

  • settingsのEMAIL_BACKENDを使う方式(mandrillなんかがそう)
  • 各マネージドサービスオリジナルのsdkを経由してweb apiを呼ぶ方式(SendGrid、AWS SESなんかがそう)

がある。

前者の場合はドキュメント Sending email | Django documentation | Django を読むと対策はされているようだ。後者はSMTPではないため気にする必要はなさそう。そしてマネージドサービス側にはそんな脆弱性が無いと信じるしか無い。。

ちょっと蛇足だけど、Sending email | Django documentation | Djangoを見るにDjangoビルトインのEMAIL_BACKENDがあることを知った。これは単体テストやローカルでのテスト時に使えそう。

ファイルアクセスにまつわる問題

Djangoに関してはurls.pyで扱ってくれるURLについては気にする必要はなさそう。Djangoとディレクトリトラバーサル - 偏った言語信者の垂れ流しを見るにPythonの一般論としてos.path.joinは気をつける必要がある。

nginxは既定では autoindex off。参考:Module ngx_http_autoindex_module。なんだけど、Djangoのstaticファイルをnginxでhostする方法について検索するとautoindex onの例がやたら出てくる。謎。

次回は年明けです。

nseg.connpass.com

「ゼロから作るDeep Learning」を自分のペースで読む会 in ギーラボ 参加記録

これはJSL (日本システム技研) Advent Calendar 2018 - Qiitaの19日目の記事です。

昨日以下のイベントを開催しました。参加者は3名でした。

glnagano.connpass.com

今回で2回目ですが前回私は都合が悪く出られなかったので今回が初参加です。最初から読みました。1章のPython入門はざっととばして2章のパーセプトロンと3章の3.3多次元配列の計算まで読みました。 内積なんて20年ぶりくらいに聞いた。。。まだついていけます。この先読み進むとどうか分かりませんけど。

「自分のペースで読む」ということで、参加者それぞれもくもくして、最後にお互いどの辺まで読んだとか所感を言い合って終わりました。 ずっと積読になっていた本でこの機会を使って読了したいです。

次回は年明けです。

glnagano.connpass.com

Django2系でのunit testにおけるno migration設定

この記事は、Django Advent Calendar 2018 - QiitaおよびJSL (日本システム技研) Advent Calendar 2018 - Qiitaの17日めの記事です。(相乗りすみません)

こちらの続き。

koty.hatenablog.com

結論としては、以下で実現できる。

from .base import *

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',
    }
}

# MIGRATION_MODULESは各installed_appsにある名前をkeyに、当該appのmigrationファイルのモジュール(既定ではmigrationsモジュール)をvalue指定した辞書。
# 以下のクラス、どんなキーを指定しても辞書に存在し、どんなキーを指定しても`None`を返す辞書を作っている。
# 'None'モジュールは存在しないので、既存のmigrationファイルは見つからずにmigration未済みという扱いになる。
class DisableMigrations(object):
    def __contains__(self, item):
        return True

    def __getitem__(self, item):
        # return 'notmigrations'
        return None  # django 1.11および2系にも対応するにはこちらで

MIGRATION_MODULES = DisableMigrations()

上記1.11系に対応したときの記事でも FlaseではなくNoneを返しておけば問題なかった。実際、これらの処理をパッケージ化したdjango-test-without-migrationsでは特段の2系対応は入っていない。そもそもこのパッケージを入れておけば良いというウワサもある。。。

DisableMigrationsはDjango内部の挙動をだまくらかすようなhack的なことをしているため、今後のDjangoのバージョンアップで動かなくなる可能性は依然あります。

というわけで、テストを高速化してハッピー開発ライフを送ってください。

Django ManyToManyFieldのthrough属性を調べた

この記事は JSL (日本システム技研) Advent Calendar 2018 - Qiita 14日目の記事です。

今年のDjango Congress のセッションでManyToManyFieldにthrough属性なるものがあることを知りました。不勉強ですんません。

through属性を使うと独自に定義した中間モデルを使えます。中間モデルに有効日from-toや有効フラグなどを指定できるようになるわけです。存在を知っただけで試して見なかったので、アドベントカレンダーのネタづくりも兼ねて試してみます。

以下のようにモデルを作りました。djangoのバージョンは2.1.4、Python 3.6.3 です。

class MyUser(models.Model):
    username = models.CharField(max_length=100)
    user_avatar = models.ForeignKey(
        'Avatar',
        null=True,
        on_delete=models.PROTECT
    )


class Team(models.Model):
    name = models.CharField(max_length=100)
    members = models.ManyToManyField(MyUser, through='TeamAssign')


class TeamAssign(models.Model):
    user = models.ForeignKey(MyUser, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.CASCADE)
    enable_from = models.DateField()
    enable_to = models.DateField(null=True)

    class Meta:
        unique_together = (('user', 'team', ), )

通常のManyToManyFieldであれば models.ManyToManyField(MyUser) としますが、中間モデルを明示的に定義し through 属性で指定しています。 とりあえず モデルを作っていきます。

teamA = Team.objects.create(name='A Team')

scott = MyUser.objects.create(username='scott')

tiger = MyUser.objects.create(username='tiger')

次に普通にモデルを追加してみます。

teamA.members.add(scott)

すると

AttributeError: Cannot use add() on a ManyToManyField which specifies an intermediary model. Use api.TeamAssign's Manager instead.

ダメでした。そこで

from datetime import datetime as dt
TeamAssign.objects.create(team=teamA, user=scott, enable_from=dt(2018,12, 16))
TeamAssign.objects.create(team=teamA, user=tiger, enable_from=dt(2018,12, 17))

で登録できました。中間モデルを明示的にcreateする必要があるようです。仕方ないといえば仕方ない。

次にデータを取得してみます。

teamA.members.all()

すると

<QuerySet [<MyUser: MyUser object (1)>, <MyUser: MyUser object (2)>]>

2件取れました。通常のManyToManyFieldと同じ使い勝手です。 enable_from <= 今日 を満たすmembersを取ってみます。

teamA.members.filter(enable_from__gt=dt(2018, 12, 16))

すると

FieldError: Cannot resolve keyword 'enable_from' into field. Choices are: custompks, id, profile, team, teamassign, user_avatar, user_avatar_id, username

無理だった。そりゃそうか。。。all()で全部取ってきてからフィルタをかけるしかないっぽい。

超便利ってほどではないけど、ManyToManyFieldを使わず自力で定義するよりはマシってとこでしょうか。

今回のサンプルコードはこちら

element-uiのform validationで他のフィールドを参照しつつ項目のvalidateをする

この記事は JSL (日本システム技研) Advent Calendar 2018 - Qiita 11日目の記事です。

突然ですがvue.jsの話題です。Element-UIのフォームでvalidationする際は普通はrulesの各フィールドにvalidatorを指定して任意のチェックをすれば事足ります。

なんですが、formのドキュメントにある通りこのvalidatorの引数は (rule, value, callback)でプリミティブな値であるvalueをチェックするほかなく、ほかのフィールドを参照できません。なので、ドロップダウンやラジオボタンの選択状況により入力必須か否かが変わる場合にvalidatorだと適切にチェックできません。

その場合は、form-itemのerrorというpropsに任意のタイミングでエラーメッセージを設定することができるので、それで実現します。

この方法を見つけるまでにけっこう時間がかかってしまったのですが、form-itemの当該propsのドキュメントには

field error message, set its value and the field will validate error and show this message immediately

とあり、今となってはうんまあそうだよなとは思うのですが、英語なこともあり最初にこれに行き着くことはなく、ソースを読んで見つけました。

サンプルを下記に起きました。

jsfiddle.net

Vue.js入門 基礎から実践アプリケーション開発まで

Vue.js入門 基礎から実践アプリケーション開発まで

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

Nuxt.jsビギナーズガイド―Vue.js ベースのフレームワークによるシングルページアプリケーション開発

nginx+django storage s3 backendでdjango本体と同じドメインを使い、かつIPによるアクセス制限をかける

この記事は JSL (日本システム技研) Advent Calendar 2018 - Qiita の7日目の記事です。

以下の要件がありました。

  • APIへのアクセスをIP制限したい
    • APIはnginx+gunicorn(Django, Django Rest Framework)で動いている
    • APIの戻り値の中にS3に置いた画像へのURLがある。
    • 画像はImageField+Django storage+S3 backendを使って保存している
  • APIと画像のドメインは同じにしたい
  • このURLもAPIと同様にアクセスをIP制限したい

順を追って設定していきます。3番目の要件は1番めと2番目が実現できればおのずと実現できそうです。

APIへのアクセス制限

AWSのセキュリティグループで80と443ポートについて制限

ImageField+Django storage + S3 backendの基本的な使い方については割愛します。

APIと画像のドメインを同じにする

settigns.pyにAWS_S3_CUSTOM_DOMAIN = 'www.example.com' を追記します。

この設定についてはdjango storageのS3 backend Amazon S3 — django-storages 1.7.1 documentation に記載されています。CloudFrontの項にあるのですが、CloudFrontを使うかどうかは特に関係ないようです。単にImageFieldのホスト名がここで設定した値になります。http://https:// は書きません。既定でhttps://が付加されます。検証環境など http://で使いたい場合は AWS_S3_SECURE_URLS = False をsettings.pyで設定します。

画像のURLへのアクセスをS3に向ける

nginx.confを設定します。

        location ~  ^/images/ {
            proxy_buffering        off;
            resolver 8.8.8.8;
            proxy_pass http://my-bucket-name.s3-website-ap-northeast-1.amazonaws.com;
        }

参考: Nginx のDNS 名前解決とS3 やELB へのリバースプロキシ :: by and for engineers

S3をEC2からのアクセスのみに制限する

VPCエンドポイントなるものを設定します。

参考:nginxとS3を使って静的コンテンツを利用者を限定した形で利用できるようにする | DevelopersIO

上記の記事ではバケットポリシーに aws:sourceVpcVPCのIDを指定していますが、VPCエンドポイントを作って aws:sourceVpceVPCエンドポイントIDを指定しないとうまくいきませんでした。

画像のURLをAPIと同様にアクセス制限する

以上の設定をしていると、画像のURLをアクセス制限できます。