ちゃぱブログ / エンジニアリング / マネジメント

とあるプロダクトの運用組織のマネジメントしてる人の雑記。主にエンジニアリングやマネジメントのことを書きます。ときおりプチ情報も

(Python)range、enumerate、zip、itertoolsを使ったループ例

はじめに

Effective Python」を読む中での自分用のメモ。「range」「enumerate」「zip」あたりの挙動について整理する。 本でいうと項目7、8あたりの内容。

range

https://docs.python.org/ja/3/library/stdtypes.html#range

ただ繰り返すときによく利用する。例えば10回for文を回す、みたいなときによく使う。

for i in range(10):
    print(i)

みたいな。 とりあえず回数分処理させたいだけならこれでOK。

enumerate

https://docs.python.org/ja/3/library/functions.html#enumerate

何回目のループかをループ内で把握しておきたい場合に利用する。enumerateの展開結果はジェネレータである。

hoges = ["a","b","c"]
gen = enumerate(hoges)
print(next(gen)) # (0, 'a')
print(next(gen)) # (1, 'b')
print(next(gen)) # (2, 'c')

これをアンパックすると、以下のような使い方ができる。

hoges = ["a","b","c"]
for i, hoge in enumerate(hoges, 1):
    print(f"{i}番目の文字は{hoge}です")
# ※以下の文字が出力される
# 1番目の文字はaです
# 2番目の文字はbです
# 3番目の文字はcです

zip

https://docs.python.org/ja/3/library/functions.html#zip

ふたつのリストを並列に処理させるときに利用する。

days = ["9/16", "9/17", "9/18"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in zip(days, weathers):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です

こんな感じ。それぞれ別のcsvファイルからデータを取ってきてがっちゃんこしたいときとかに使えそう。 注意点としては、zipで取り扱うリストは密な関連があること。どちらかが変わると影響を受ける。 片方が短い場合には、短いほうに合わせて出力されるので、注意が必要。上に書いた複数のcsvをがっちゃんこするようなケースで、片方がデータを1行ロストしていたりすると、もう片方も消える。そして気づきにくい・・・ そんなときのため?には、itertools.zip_longestを利用する。

itertools.zip_longest

https://docs.python.org/ja/3/library/itertools.html#itertools.zip_longest

zipの項目で記載したようなサイズが異なるリストをくっつけるときに利用する。 こんな感じ。比較のためにzipの挙動も再掲。 fillvalueを入れておくと、それが出力される(これは天気が少ないのを見越したfillvalueになってしまったが)。 defaultは "None" と表示される。

days = ["9/16", "9/17", "9/18", "9/19"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in zip(days, weathers):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です

#--------------------------#
import itertools

days = ["9/16", "9/17", "9/18", "9/19"]
weathers = ["晴れ", "曇り", "快晴"]
for day, weather in itertools.zip_longest(days, weathers, fillvalue="天気がわからない"):
    print(f"{day}は{weather}です")
# 9/16は晴れです
# 9/17は曇りです
# 9/18は快晴です
# 9/19は天気がわからないです

おまけ

上記を踏まえて、以前に書いたコードがenumerateで置き換えられるなぁというのを反省例として書いておく。 実際はDjangoのshell経由でFactoryBoy使ってDBにデータ登録するものだったけど、少し簡略化して記載。

class Word:
    def __init__(self, word_id:int, word:str):
        self.word_id = word_id
        self.word = word

class IdNumber:
    def __init__(self):
        self.number = 0
    def get_number(self):
        self.number += 1
        return self.number

# Wordの作成
word_counter = IdNumber()
Word(word_id=word_counter.get_number(),word='Django')
Word(word_id=word_counter.get_number(),word='Git')
Word(word_id=word_counter.get_number(),word='Python')
Word(word_id=word_counter.get_number(),word='Go')
Word(word_id=word_counter.get_number(),word='Oralce')

わざわざカウンターなんて作っていたのだけど、こんな簡単に書き換えれる。データを足したいときにも、わざわざ1行足さずにリストに追加するだけで済む。うーん、無知を恥じる。

class Word:
    def __init__(self, word_id:int, word:str):
        self.word_id = word_id
        self.word = word

wordlist = ['Django', 'Git', 'Python', 'Go', 'Oracle']
for i, word in enumerate(wordlist, 1):
    Word(word_id = i, word = word)

(Django備忘)render、reverse、reverse_lazyの違いを簡単にまとめておく

概要

Djangoのrender、reverse、reverse_lazyってよく使うけど、それぞれどんな用途のものなのだ?をまとめた自分用のメモ。 よくわからず使ってたのだけど、調べてみると、そもそも並べて比較する対象ではなかった。

雑な 結論

コマンド 分類 概要
render HttpResponseを返すもの 単純なテンプレートとしてHttpResponseをreturnするときのショートカット
reverse URLの解決 url.pyで決めた名前を解析する関数
reverse_lazy URLの解決 reverseをクラスベースビューのクラス変数として書くときに利用

少し詳細

render

こちらのリンク先に、

テンプレートをロードしてコンテキストに値を入れ、テンプレートをレンダリングした結果を HttpResponse オブジェクトで返す、というイディオムは非常によく使われます。 Django はこのためのショートカットを提供します

とある。 ベーシックにテンプレートを返すときに利用する。なので、リダイレクトさせたいときは別。(redirectを利用する)

reverse、reverse_lazy

「URLを解決する」という点は同じ。ただ、クラス変数の定義の際には、まだurls.pyが動いていないので名前が定義されていない。それを解決するために、遅延関数としてreverse_lazyを用意している。 つまり、クラスベースビューなどで、

class HogeCreateView(CreateView):
    success_url = reverse_lazy(‘app:index’)

といったときに、reverse_lazyを使う。 関数の中でURLを解決したいときは reverse で良い。

renderと本来比較すべき子たち

Djangoのショートカット 本来は上のページにあるような、redirectとかを比較対象とすべき。ほんと何もわかってなかったなー。反省。

参考にさせていただいた記事

[Django] success_urlとget_success_urlおよびreverseとreverse_lazyの使い分け

Data Tablesの表中にリンクを埋め込む

はじめに

django-datatables-viewを使ってAjaxで便利な一覧を作ることには成功したものの、そのままだと使いにくい。なぜなら一覧から詳細画面に遷移する、などのリンクがなかったから。これはどうしても必須だったので、調べて実装した。以下の記事の続きの位置づけ。

as-chapa.hatenablog.com

結論

「render_column」をオーバーライドして、指定の項目のときだけreturnを変えるようにする。 ソースはこちら。

github.com

実装としては以下の通り。

class BookJsonView(BaseDatatableView):
    model = Book
    columns = ['id', 'title', 'author']

    def render_column(self, row, column):
        if column == 'id':
            return f'<a href="/sample/bookdetail/{row.id}">{row.id}</a>'
        else:
            return super(BookJsonView, self).render_column(row, column)

「id」のときだけ、リンクとなるようにHTMLのタグをreturnした。

get_absolute_url()の定義でもうまくいくと思いきや・・・

django-datatables-viewのソースを見たところ、Modelにget_absolute_url()メソッドが書いてあると aタグとして返すように書いてある。

def render_column(self, row, column):
    """ Renders a column on a row. column can be given in a module notation eg. document.invoice.type
    """
    value = self._render_column(row, column)
    if value and hasattr(row, 'get_absolute_url'):
        return format_html('<a href="{}">{}</a>', row.get_absolute_url(), value)
    return value

Bitbucket

ほうほう、なるほど、きっとこっちの実装のほうがif文で拾う必要もなくキレイになるなぁと思い試した。

models.py

from django.db import models
from django.urls import reverse


class Book(models.Model):
    """本"""
    class Meta:
        db_table = 'book'
    
    title = models.CharField(verbose_name="本のタイトル", max_length=255)
    author = models.CharField(verbose_name="著者", max_length=255)

    def get_absolute_url(self):
        return reverse('sample:bookdetail', kwargs={'pk': self.pk})

こうすると、 views.py でわざわざ if column == 'id': なんてダサいif文をかまさなくて良くなる!と思ったが、最終形がイメージと違った・・・別に全部にリンクはいらないんだよな。というわけで、自分は「結論」に書いた実装とした。

f:id:as_chapa:20200905080204p:plain

参考までに全ソースはタグとして保存しておいた。

Release tag_get_absolute_url_sample · as-chapa/datatables · GitHub

Djangoはじめました(2020年8月)

はじめに

2020年6月にDjangoを業務外で触り始め、亀の歩みで勉強したことの記録を残す。過去分の記録はこちら。

Django月記 カテゴリーの記事一覧 - ちゃぱ日記 / エンジニアリング / マネジメント

やったこと

体系立てたインプット

できるだけDjangoの公式サイトを見ながら学ぶように心がけていた。しかし日本語訳がないページがある、検索が使いにくいなどから、なかなか欲しい情報にたどり着けず、Googleでどなたかのブログを参考にする時間が増えてきた。 そんな中で、改めて体系立てて学びたいなと思い、期待を持ちつつ買った「現場で使えるDjangoの教科書 [基礎編]」が期待の何倍も良くてじっくり読んだ。とにかくわかりやすいし、痒いところに手が届いている。「現場で使える」は嘘ではない。今でも何度も読み返している。

booth.pmAmazonでも買えます)

Datatablesの実装

6月に作っていたアプリで、ページングを作りこむのが面倒だなと感じていた。ただ、6,000件ほどのデータがたまっており、ページングを作りこまないと使えないのも明らかだった。同僚からJavascriptのライブラリで簡易に実装できそうなことは聞いていたので、調べてみるとData Tablesがよさそうかなという結論に至った。6,000件ならどうにかなるのかもしれないが、これからもデータが増え続ける予定だったので、いつか処理性能に問題が出るのでは?と思い調べていたところ、「django-datatables-view」というライブラリで簡単にAjaxで実装できそうだったのでトライした。 こちらは別途記事にもした。

as-chapa.hatenablog.com

勉強用のWebアプリ開発

8月の後半からは、Data Tablesのお試しと並行して、また別のアプリを作り始めた。これまではテーブルの結合が必要となるものを作っておらず、もう少し複雑なものを、と同僚とお試しで始めた。まだまだ実装初期なので、9月はきっとこれに注力するはず。

その他

おそらく9月に勉強していく中で、こちらの記事は参考になるはずなので、備忘も兼ねて掲載しておく。

www.slideshare.net

データがたまっても平気な高機能な一覧をData Tables&Djangoで開発(Ajax利用)

概要

一覧の検索やソート、ページングなどを自分で実装するのはなかなかに骨が折れます。いくつかの便利なライブラリがあり、そのひとつであるData TablesをDjangoで使う実装例を紹介します。

ただ、そのまま実装するとデータ量が多くなったときにJavascript側での動作が重くなってしまいます。なので、本記事ではajaxを使ってデータ量が多くなっても対応できるように実装しました。

この記事は、Data Tablesでどのようなことができるのかを詳しく解説するものではなく、まずは動くものができるところまでを体感してもらうことを優先しています。

完成系のイメージはこのような感じです。

f:id:as_chapa:20200828174614p:plain
Data Tables完成イメージ

完成系のソースはこちらに置いてあります。 github.com tag:v1.0

イチから作る場合の手順

前半はほぼほぼDjangoの話になってしまいます。

DjangoとData Tables関連ライブラリのインストール

まずはDjangoとData Tablesを利用するためのライブラリをインストールします。

$ pip install django==2.2
$ pip install django-datatables-view==1.19.1

※バージョンは多少変わっても動くと思います

Djangoの初期設定&アプリ作成

次にアプリケーションを作ります。

$ django-admin startproject config .
$ django-admin startapp sample

このままだとsampleが動いてくれないので、 settings.py のINSTALLED_APPSにsampleを追加します。

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'sample' # ←これを追記
]

データベース作成

お次は一覧に表示するためのDBを格納するために、簡単なModelを作るために models.py を変更します。

from django.db import models

# Create your models here.
class Book(models.Model):
    """本"""
    class Meta:
        db_table = 'book'
    
    title = models.CharField(verbose_name="本のタイトル", max_length=255)
    author = models.CharField(verbose_name="著者", max_length=255)

本のタイトルと著者名を登録するためだけの簡単なテーブルです。

models.py ができあがったところで、マイグレートします。今回はData Tablesを試してみることが目的なので、DBはSQLiteとします。(DjangoはデフォルトでSQLiteが指定されるので、特に気にすることはないです)

$ python manage.py makemigrations
$ python manage.py migrate

ついでにテスト用のデータも投入します。

$ python manage.py shell < sample/create_testdata.py

URLディスパッチャ作成

まずは、 config/urls.py にsampleへのディスパッチ行を追加します。

urlpatterns = [
    path('admin/', admin.site.urls),
    path('sample/', include('sample.urls')) # ←ここを追加
]

続いてアプリの配下にurls.pyを作成します。

from django.urls import path

from . import views

app_name = 'sample'
urlpatterns = [
    path('booklist/', views.BookList.as_view(), name='booklist'),
    path('booklist_ajax/', views.BookJsonView.as_view(), name='booklist_ajax'),
]

ここで、Data Tablesで呼び出すDjango側のエンドポイントを指定しておきます。今回の例では booklist_ajax としました。

ビューの作成

ビューを作ります。 django-datatables-view を利用しているので、ajaxのエンドポイント部分はとてもシンプルにできています。

from django.views import generic
from django.views.generic import ListView
from django_datatables_view.base_datatable_view import BaseDatatableView

from .models import Book

class BookList(ListView):
    model = Book
    template_name = 'sample/book_list.html'

class BookJsonView(BaseDatatableView):
    # モデルの指定
    model = Book
    # 表示するフィールドの指定
    columns = ['id', 'title', 'author']

    # 検索方法の指定:部分一致
    def get_filter_method(self):
        return super().FILTER_ICONTAINS

BookList は一覧全体を表示するためのもので、特段捻りもなくクラスビューで構築しました(これはListViewじゃなくても良かった気がします)。 BookJsonView がData Tables用になります。対象のテーブルを、 model で指定し、その中から一覧で表示したい項目を columns で指定します。 FILTER_ICONTAINS はマストではないのですが、部分一致で検索するために追加しています。

やっと見た目のところに到達・・・

色々とbase.htmlを作ったりとしているのですが、肝となる記述は、 template/sample/book_list.html の以下の箇所です。

<div class="container">
    <table id="datatable" class="table table-striped table-bordered table-hover dataTable no-footer dtr-inline">
        <thead>
            <tr>
                <th>No.</th>
                <th>本名</th>
                <th>著者名</th>
            </tr>
        </thead>
    </table>
</div>

ここは、Data TablesのJavascriptでテーブルを完成させるための箇所です。ヘッダーだけ書いておきます。ここで注意しなくてはいけないのが、<th></th> の数が、views.pyのcolumnsで選択した数と一致することです。 あとは、ajax部分を以下のように書けば勝手にajax向けのエンドポイントにリクエストを投げて、返却されたjsonをいい感じにJavascriptで処理してくれます。 (ほんと便利)

<script>
$(document).ready(function() {
    var oTable = $('#datatable').dataTable({
        // ...
        "processing": true,
        "serverSide": true,
        "ajax": "{% url 'sample:booklist_ajax' %}" // 上で作成したエンドポイント
    });
    // ...
});
</script>

地味なノウハウとして、今回Bootstrap4を使っているのですが、Bootstrap4のオフィシャルページで指定されるjQueryは「slim」というバージョンで、これだとajaxが動かないです。ですので、 base.html の以下のところをslimじゃないものに置き換えています。

    <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>

さてやっと動かす日がきた・・・

ここまででおそらく動くようになったはずなので、

$ python manage.py runserver

として、エラーが出ないことを確認したら、 http://127.0.0.1:8000/sample/booklist/ にアクセスしてみてください。 冒頭に張り付けたキャプチャのような画面が表示されるはずです。

さいごに

これだけ高機能のものを自作で作ろうとするととても大変です。数日前に、ページングの実装をしようとして萎えていたので、試してみて実際に動いたときは感動しました。 見た目など融通が効かないところがありそうだなとは思うので、そこはもう少し調べていきたいと思います。

参考

おまけ

Templateの書き方や、startproject時のプロジェクト名などは、以下の書籍を参考にさせていただきました。とても勉強になる良書過ぎてDjango初心者の身分としては大変助かる書籍です。 www.amazon.co.jp

 

Djangoはじめました(2020年7月)

はじめに

2020年6月にDjangoを業務外で触り始め、亀の歩みで勉強したことの記録を残す。6月分の記録はこちら。

as-chapa.hatenablog.com

やったこと

Webアプリを作りながら勉強しているのだけど、7月に何を作ったか思い出してみると、Djangoじゃないところに時間を費やしていたことが判明・・・

  • ログイン/ログアウト機能の実装
  • Docker化(Djangoじゃない)
  • Bootstrapによる見た目の調整(Djangoじゃない)

ログイン/ログアウト機能の実装

LoginView、LogoutViewを使って作ったのだけど、ほぼほぼコード書かなかった。urls.pyとsettings.pyにちょろっと書いただけだったような。恐ろしく速攻で実装できて感動した覚えがある(その感動を忘れてきている・・・)。

コンシューマー向けの実プロダクトでこのままはさすがに使えないけど、社内ツールレベルだったら問題ない。PWの暗号化などもデフォルトで実装されているし、最低限は守られていそう。

Django本体ではないのだけど、こちらでボット的なログイン試行を止める機能が追加できるよう。

django-axes · PyPI

Docker化

Djangoの勉強がメインなので、Dockerにする必要はないんだけど・・・せっかくだからと勉強がてらDokcerで構築もしてみた。

DBは外部のを使う前提で、さらに手動でmigrate済じゃないと動かないので、微妙なDockerfileになってしまった。時間取れたらこのあたりもちゃんと整理しよう。

Bootstrapによる見た目の調整

こちらのYoutubeで少し勉強したので、一部でGridを導入したり。この方の動画は丁寧でわかりやすい。

www.youtube.com

完全に余談で、Emmetの素晴らしさに気づかされたのも、このYoutubeのおかげ。初めて知ったけど、感動した。

おわりに

完全に自分用のメモになってしまった。8月もあまり大きな進捗はないんだよなぁ。(遠い目・・・)

Webアプリ作ってると、見た目のところの改善に取り組みたくなりがちで、ついついJSのライブラリ探してみたりと、Djangoじゃないところに浮気しそうになる。よくない。

ちょうど先日「現場で使えるDjangoの教科書 基礎編」を読んで学びが多かったので、そこから気になったものを実コードに落とすか。

「みんなでアジャイル」を読んで

はじめに

fukabori.fmのエピソード32で紹介されていた本「みんなでアジャイル」。「アジャイル」には興味はあったものの、本で学んだことがある程度で、例えばスクラムやXPなどのプロセスを実践したことはない。ちょっと興味ある程度の気持ちで聴きながら、「あれ、これ組織の中でよくある問題のこと話してないか?」という感覚を持ち、その中でも「組織の対立」という点で気づきがありそうだと思い買った。

fukabori.fm

「みんなでアジャイル」とは?

Matt LeMayさんの書籍をRyuzeeさんたちが翻訳し、オライリーから出版されたアジャイルに関する本。

アジャイルのプロセスというよりは、マインドセットも含めて、どのように組織にアジャイルをフィットさせていくか?ということが書いてある。プロセスを知りたい人向けではない。

オライリーのページには以下のような記載とともに紹介されている。

市場の急激な変化に適応し、バリューチェーン全体を最適化したい組織のマネージャ、非エンジニアリングチームのメンバー、エンジニアと協業するすべての人に必携の一冊です。 

 

www.oreilly.co.jp

この本に求めていたこと

正直「アジャイル」についてよりは、組織の対立構造に対して解決に導く考え方のヒントがないかな?ということを求めていた。

ビジネスがスケールしてきたときの組織の最適なサイズ感が難しいと感じている。機能的に分けると効率が良いが、全体感を捉えられない。一方でビジネスがスケールしたときに、個人が見れる範囲の能力的な限界がきて分割せざるを得なくなる。そのときに、分割したチームが有機的につながって成果を出していくにはどうしたら良いのだろうか?

といったことを、あれこれ考えるのが好きなので、この本がそのヒントになるのでは?という期待があった。(fukabori.fmを聴いていたらそんな気持ちになった)

得られたヒント、そしてモヤモヤ

プロジェクト(終わりがあるもの)なのか、運用・保守のような継続的なものなのかでも違う気はする。プロジェクトの場合には、書籍の中でのヒントとして「横断的なチームを作る」「同じ場所で仕事をする」「初期段階から巻き込む」といったようなことが書いてあった。わかる。

ただ、実際の組織でこういう動きをしようとすると、難しい組織が多いんじゃないか?という肌感がある。いっそのこと、トップダウンで落としてもらったほうが、形としては作りやすい。ただ、形だけ作ってもうまくいかない。

「難しいんだろうなぁ」という肌感がどこから生まれてくるのか?と思うと、「組織内の個々人の感覚が揃ってないと」という前提がつくからなんじゃないかなと思った。

これについても書籍の中で気づきを得られるようなことが書いてある。

報告と批評の文化から協調的な文化へ(P.68)

「協調的な”文化”」というのが大切。文化。文化になるまで、個々人に浸透している。ということ。「群集心理(ギュスターヴ・ル・ボン)」にも書いてあった気がしたのだけど、個々人の長年の蓄積によって文化は醸成される。時間はかかるし、時間をかけて作り上げたものだから、変えるのも時間がかかる。これが「難しいんだろうなぁ」と感じる理由かなと思う。組織の規模が大きければ大きいほど。

その他のヒント

ゴール設定をうまくやることで、解決に導くことができる。というのも感じた。

こんな一文がある。

達成したいゴールと解決しなければいけない課題を明確に理解できれば、それぞれの戦術ニーズを満たせるように裁量を与えることができる(P.130)

チームをわけたりすると部分最適に走りがちになりそこから対立が生まれると感じるけど、全体の方向感を見定めながら各チームのミッションとして落としていくとうまくできるのかなと思う。トップというかマネジメント層の腕の見せ所だな。

おわりに

全然アジャイル関係ない記事になったし、「みんなでアジャイルを読んで」というタイトルから想起させる記事でもなくなったけど、自分の悩みと、この本から得られたヒントのようなものを記してみた。もうちょっとこのテーマは整理していきたい。