DjangoMeetupTokyo #13を開催しました

2024/4/13にDjangoMeetupTokyo #13を開催しました。

DjangoMeetupTokyo #13 - connpass

前回同様、もくもく会+ハンズオンの型式での開催をしました。

中級者向けハンズオン

前回同様、私が資料を準備して、中級者向けハンズオンをやりました。

DjangoMeetupTokyo #13 中級者向けハンズオン — django-meetup-tokyo-13 ドキュメント

今回はDjangoのユーザー認証と、ユーザーに紐づくデータの取り扱いを学ぶ内容でした。

資料の内容を順番に説明し、手元で動かしてもらいながら、Djangoの機能について詳しく見ていきました。

本日、資料の手順を再検証、うまく動かなかったところなどを修正、説明を追記しました。

Dartの開発環境を準備する

社内ライブラリのDart版の動作検証をするために、Dartの開発環境を準備する必要があったので、自分用にまとめておく。

DartSDKのインストール

Dartのコードを実行、ビルドするためにはSDKをセットアップする。

Get the Dart SDK | Dart

今回はUbuntu環境だったので、Linux用の手順でaptを使ってパッケージをインストールした。手順通りで特に問題なし。

dartコマンド

dartの実行、ビルドなどは dart コマンドからできる。

サードパーティパッケージ

pub.devというサービスがパブリックなサードパーティパッケージのホスト先。

アプリの依存パッケージは pubspec.yaml ファイルに記載。

dart pub get コマンドで依存パッケージをインストールできる。

dartコマンドからファイルを指定して実行

main.dart ファイルを実行する場合、

dart run main.dart

dartコマンドでコンパイル

main.dart ファイルをコンパイルする場合、

dart compile <target> main.dart

<target> には exe(実行ファイル) や jsJavaScript)などを指定できる。

exeの場合、Dartのランタイムを含む実行ファイルが出力される。クロスコンパイルの機能はないが、各コンパイル環境のOSで実行可能なバイナリを生成できる。

導入から実行、ビルドまではスムーズにできたのでよかったです。

Pythonのdataclasses.dataclassを使う

Pythonのdataclasses.dataclassは普段からたまに使っていますが、良く使っている書き方を人に紹介するためにメモを残します。

dataclasses - データクラス - Python 3.12.2 ドキュメント

dictと相互変換するクラス

オブジェクトとdictで相互変換するクラスをdataclassで書くことがあります。

asdict 関数が便利です。リストで保持したいメンバー変数は、 field 関数を使って定義すれば、asdictでそのまま対象にできます。

また from_dict メソッドは厳密に実装するなら引数のチェックなどをしてもよいですが、可変長のキーワード引数としてそのままコンストラクタに渡すように書けば、実装はシンプルです。

コード

main.py:

from dacite import from_dict
from dataclasses import dataclass, asdict, field


@dataclass
class Person:
    name: str
    age: int

    @classmethod
    def from_dict(cls, data: dict):
        return cls(**data)


@dataclass
class Group:
    name: str
    members: list[Person] = field(default_factory=list)

    @classmethod
    def from_dict(cls, data: dict):
        members_data = data.get("members", [])
        instance = cls(**data)
        if members_data:
            instance.members = [
                Person.from_dict(member_data) for member_data in members_data
            ]
        return instance


def main():
    person = Person(name="Alice", age=30)
    group = Group(name="Team1", members=[person])

    # groupをdictに変換すると、membersの要素もdictに変換される
    data = asdict(group)
    print(data)

    # dictからGroupオブジェクトを作成すると、membersの要素もPersonオブジェクトに変換される
    group2 = Group.from_dict(data)
    print(group2)

    # daciteを使えば、from_dictメソッドがなくても、dataclassのネストしたオブジェクトを作成できる
    group3 = from_dict(data_class=Group, data=data)
    print(group3)


if __name__ == "__main__":
    main()

実行結果

daciteを事前にインストール済み。

$ python main.py 
{'name': 'Team1', 'members': [{'name': 'Alice', 'age': 30}]}
Group(name='Team1', members=[Person(name='Alice', age=30)])
Group(name='Team1', members=[Person(name='Alice', age=30)])

PersonとGroupのdataclassはどちらも親クラスの記述は無し。 from_dict クラスは明示的に実装しています。

fieldを読み取って自動的に from_dict の振る舞いをかえるような実装もできますが、継承関係を作らないのと、仕組みを複雑にしないためにあえてしていないです。

from_dict メソッドの実装を省略したい場合は、 dacite のようなサードパーティパッケージを使うのもよさそうです。

Djangoテンプレートで継承を利用して各ページ共通のヘッダーやフッターを設定する

Djangoフレームワークのテンプレートの継承について、基本的な内容です。

base.html というファイルを作って、共通部分をまとめる話。

検索用に記事をまとめておきます。

※この記事は 力強くアウトプットする日の 20240301 のアウトプットです。

テンプレートの継承

Djangoのドキュメントに例と説明があります。

テンプレートの継承 - Djangoドキュメント

Djangoのテンプレートには『継承』という仕組みがあります。

どのようなときに利用するか

HTMLでウェブサイトを作る場合、複数のページでヘッダー部とフッター部を共通にすることがよくあります。 また、サイドバー、レイアウトなど、複数ページに渡って共通の構造とするパターンがとても多いです。

こうした構造のときにテンプレート継承がうまくハマります。

base.html:

<html>
<head>
<meta charset="utf-8">
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
{% block content %}{% endblock %}
<script>
// 全ページ共通で差し込むJavaScript
</script>
</body>
</html>

test.html:

{% extends "base.html" %}

{% block title %}テストページ{% endblock %}

{% block content %}
<p>
  bodyタグ内のコンテンツ
</p>
{% endblock %}

views.py:

from django.shortcuts import render

def test(request): 
    return render(request, "test.html")

レンダリング結果

<html>
<head>
<meta charset="utf-8">
<title>テストページ</title>
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>

<p>
  bodyタグ内のコンテンツ
</p>

<script>
// 全ページ共通で差し込むJavaScript
</script>
</body>
</html>

テンプレート継承の利点

extendsタグで検証したテンプレートファイルでは、継承元のblockのうち、 変更したい部分 だけを記載することができます。 継承元のテンプレートに変更可能なblockがたくさんあっても場合、必要な部分だけ書けばいいので、記述量の削減につながります。

テンプレート継承の動作イメージ

比較対象として、 includeタグ があります。 includeタグは、指定したテンプレートファイルの内容を、includeタグの記載位置に取り込みます。

includeタグの動作イメージ

includeの場合、この図の例のようにヘッダーとフッターが別のファイルに分離されます。HTMLの場合、 <html><body> も共通部に書きたい場合が多いと思いますが、閉じタグの対応が別々のファイルに含まれるのは見通しが良くないです。

また、includeだけで組み立てる場合は、includeタグを記述しないと対象のテンプレートが取り込まれないので、変更が少ししかないページでも記述量が多くなりがちです。

よくつかうsshコマンドのオプション

sshコマンドのオプションは、特定の環境下で作業する際にはよく使うのだけど、しばらく使わないと忘れてしまって毎度しらべているので、自分用にまとめておく。

※この記事は 力強くアウトプットする日の 20240216 のアウトプットです。

ssh(1)のドキュメント

man.openbsd.org

-L ポートフォワーディング

書式

ssh -L [bind_address:]port:host:hostport destination

destination側から見た host:hostport を接続元の環境のportにフォワードできる。bind_addressを省略した場合はlocalhost

ssh -L 8888:localhost:8000 example.com

example.com というsshでログインできるホストの localhost:8000 = 127.0.0.1:8000 を接続元の 8888フォワード。 接続元の端末で、 localhost:8888 につなぐと、 example.com上の localhost:8000 につながる、と考えてよい。

踏み台とする場合

hostの部分はdestinationからネットワーク的に接続可能であれば、localhost以外も指定できるので、たとえばdestinationのサーバーからしか接続できない、内部ネットワーク上にあるホストに接続することもできる。

ssh -L 8888:192.168.100.123:3306 example.com

example.comというホストから見て、内部ネットワークにあたる192.168.100.123というIPv4アドレスのホストでデータベースサーバーが動作している場合(3306はMySQLのデフォルトポート)

このコマンドを実行した環境で、 localhost:8888 に接続すると、192.168.100.123:3306ポートにつながる。

もし、MySQLのコマンドから利用するのであれば、以下のようなコマンドになる。

mysql -h localhost -P 8888 -u db_user -p db_name

このように、example.comを踏み台として、その先にあるホストに接続が可能となる

-D ダイナミックフォワーディング(SOCKSプロキシー

書式

ssh -D [bind_address:]port destination

destinationを起点としたSOCKSプロキシーを、接続元の環境のportで起動する。bind_addressを省略するとlocalhost

SOCKSプロキシーはアプリケーションレベルでフォワーディングをしたいときに使う。主にウェブブラウザ。

ssh -D 8888 example.com

接続元の環境の localhost:8888 でSOCKSプロキシーが起動する。たとえば、example.com経由でしか接続できない内部ネットワーク上のウェブサイト(例: internal.example.com)をブラウザで見たい場合。

ウェブブラウザのプロキシー設定で、SOCKSプロキシーのアドレスに localhost:8888 を設定し、ブラウザのアドレスバーに internal.example.com と入力して接続すると、 example.com のホストから通信を行うことになるため、閲覧できるようになる。

Chromeの場合、プロキシー設定は Proxy SwitchyOmega などの拡張を使うと有効/無効の管理がしやすい。

-L オプションだと1つのホスト、ポートを対象としたフォワーディングだが、 -D の場合はSOCKSプロキシーに対応したアプリからは、destinationのホスト経由で任意のホストに接続できる。

-R リモートフォワーディング

書式

ssh -R [bind_address:]port:host:hostport destination

destinationのホストのportに接続すると、接続元の host:hostport につながる。-L の逆向きと考えればよい。

-X X11フォワーディング

書式

ssh -X destination

接続元環境のX11ディスプレイサーバーにdestination先のX11の接続をフォワーディングする。

destinationの環境でデスクトップアプリを起動し、画面だけを接続元環境に持ってきたい場合に使う。

たとえば、destination先のLinux環境にChromiumをインストールして起動し、接続元環境で画面の操作をしたい場合。VNCなどのリモートデスクトップ接続を利用しなくても、sshだけつながれば使える。

その他

多段SSH

destinationを経由して更に別のホストにsshで接続したい(=多段SSHしたい)場合、ProxyCommandやncコマンドを使う例がでてくるが、最近のOpenSSHの場合は、 -J オプションや ProxyJump.ssh/config)を使えば記述がシンプルになる。

DjangoMeetupTokyo #12を開催しました

2024/1/28にDjangoMeetupTokyo #12を開催しました。

DjangoMeetupTokyo #12 - connpass

去年の夏以来の開催でしたが、今回は前回の倍ぐらいの参加者が集まりました。

コロナ禍が落ち着いてきて、勉強会などのイベントで基本的に制限がなくなったので、コロナ禍以前には活発だったオフラインの会を復活させていきたいなぁと個人的に思っています。

中級者向けハンズオン

今回、私が資料を準備して、中級者向けハンズオンというのをやりました。

DjangoMeetupTokyo #12 中級者向けハンズオン — django-meetup-tokyo-12 ドキュメント

説明にある通り、「チュートリアルは理解できている、Djangoは使ったことあるけど、使いこなせてない~という人向けのハンズオン」というのを以前からやりたいなと考えていたものを、ようやく実施できました。

資料の内容を順番に説明し、手元で動かしてもらいながら、Djangoの機能について詳しく見ていきました。

好評のようでしたので、他の題材も考えて、またやれればよいなーと思います。

ようす