@kotyのブログ

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

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をアクセス制限できます。

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

nseg.connpass.com 業務多忙につき、2ヶ月ほど間を空けてしまった。参加者は5名だった。

トピックとしては、「リダイレクト処理にまつわる脆弱性」と「クッキー出力にまつわる脆弱性」。 前者についてはヘッダーに不正な改行コードを入れられたときに発生する。

Django(1.8.3)で以下のコードを試してみたところ、BadHeaderErrorでHTTPステータスコードが500が返ってきたのでちゃんと対策されていそう。安心安心。

@require_GET
def index(request):
    response = render(request, 'index.html', {})
    response['MY_HEADER\nHAGE_HEADER'] = 'hogehoge'
    return response

次回はイベントがかぶったので一日ずらしました。12/13(木)です。本書は各項が比較的独立しているので、途中参加しやすいと思います。お気軽にご参加ください。 nseg.connpass.com

無題

今年もあと2ヶ月だ。ふとこの1年のエントリーを振り返ると、何だかんだで毎月記事を書いていた。勉強会への参加記録とかカウントするのにはばかられるものも結構あるけど。。

技術記事はインフラ系が多かった。もう少しフロント関係とかデータコネコネ関係もアウトプットしたいところ。

PyCon JP 2018 に参加してきた

参加された皆さま、スタッフの皆さま、お疲れさまでした。簡単ですが参加報告です。

聴講したセッション

基調講演:Argentina in Python: community, dreams, travels and learning

動画:https://www.youtube.com/watch?v=KwmF5wyY2C4

Pythonに出会って良さに惚れ込み、南米中を車で巡って勉強会をしている人の話だった。ものすごい情熱。国を超えても同じスペイン語が通じる点は日本と状況が違うな。。

PyCon JP における子ども向けワークショップの活動事例と実施の意義

動画: https://www.youtube.com/watch?v=OTydTBD3_04

Building Maintainable Python Web App using Flask

リファクタ前に例示したコードがSQLを組み立てるのにstring.formatを使っていた。まあコード例だと思うんで。。。

どこまでmockを使うかは悩みどころかと。

DBアクセス部分はDjangoだったらsettings.pyで切り替えてインメモリのsqliteを使うのが良いと思う。このセッションはORM使う前提じゃないのでいろいろ面倒なことをする必用があった。

Pythonで時系列のデータを分析してみよう。

動画:https://www.youtube.com/watch?v=k7kE0P0oYUk

当社社員の発表。盛況で何より。

Pythonで解く大学入試数学

動画:https://www.youtube.com/watch?v=x0mIzF_VXLg

sympyなんてものがあるとは知らんかった

二日目基調講演

動画:https://www.youtube.com/watch?v=kO4FNg648qE

HomeSecurity with Python

動画:https://www.youtube.com/watch?v=MCRDLld6kXU

当社社員の発表。こちらも盛況で何より。

Djangoだってカンバンつくれるもん(Django Channels + Vue)

動画:https://www.youtube.com/watch?v=RTIPoW21K3U

Make a Drone using RaspberryPi and Google VoiceKit by Python

動画:https://www.youtube.com/watch?v=5TYodp-e-9o

昨年も同じネタで発表した人。会社作ってた。

Pythonで始めるウェブスクレイピング実践入門

動画:https://www.youtube.com/watch?v=zgTP_4-XEpw

当社社員の発表。こちらも盛況で何より。

所感

  • 今年は当社からスピーカーとして3名登壇した。自分もプロポーザル出したけど採択にはいたらず。
  • 英語がんばろう。
  • 全体の質疑応答で質問しないで、終了後に個別に質問するのはよくない。QAを共有した方が良いのに。
  • 確かに例年よりDjangoネタは多かった。理由は不明。
  • その分データ分析系は思ったよりも少なかった。この分野も落ち着いてきて一般化したってことなんだろうか。
  • 機械学習・データ分析関係の話題はまったくついていけない。何か始めないとなぁ。。

あとで見る

自分へのメモ。この辺りか。。。