mizzsugar’s blog

日々感じていることや学んだことを書きます。エンジニアリング以外にも書くかもしれません。

DjangoのフォームのChoiceFieldに画像を挿入する

苦労したので、備忘録に・・


環境

※今回投稿する方法は、Django1系では利用できません。 1系はこちらをご参照ください。 なお、下記の記事の中にあるRadioFieldRendererは2系では存在しません・・・

moqada.hatenablog.com

注文アプリを作成している時に、モデルから選択肢を作成し、モデルに格納しているimgを挿入したいという件がありました。


完成図

f:id:mizzsugar:20181205223208p:plain

models.py

class Curry(models.Model):
    name = models.CharField(max_length=30)
    price = models.IntegerField()
    image = models.FilePathField() 


class Order(models.Model):
    user_name = models.CharField(max_length=20, unique=True)
    curry = models.ForeignKey(Curry, on_delete=models.CASCADE)
    amount = models.IntegerField()

forms.py

def generate_curry_choice() -> Iterable[Tuple[int, str]]:
    @dataclasses.dataclass
    class MyLabel:
        name: str
        image: str
        price: int

        def __str__(self):
            return self.name

    return (
        (curry.id, MyLabel(curry.name, curry.image, curry.price))
        for curry in Curry.objects.all()
    )


class OrderForm(forms.Form):
    user_name = forms.CharField(label='名前', max_length=20)
    curry = forms.ChoiceField(
        label='カレー',
        choices=generate_curry_choice,
        widget=RadioSelect
    )

order.html

<form action="/order_form/{{ group.url_uuid }}" enctype="multipart/form-data" method="POST">
        {% csrf_token %}
        <div class="form-group">
          <label>{{ form.user_name.label }}</label>
          {{ form.user_name }}
          {{ form.user_name.errors }}
        </div>
        <div class="form-group">

            {% for radio in form.curry %}
            <div class="curry-select">
            {{ radio.tag }}
            </div>
            {% endfor %}
            {{ form.curry.errors }}
        </div>
        <button type="submit" class="btn btn-primary btn-lg btn-block">登録</button>
    </form>



画像をいれるスキがない!!!

widgetのRadioSelectについて公式ドキュメントで調べてみると・・・

class RadioSelect(ChoiceWidget):
    input_type = 'radio'
    template_name = 'django/forms/widgets/radio.html'
    option_template_name = 'django/forms/widgets/radio_option.html'

Djangoにあるhtmlを利用してフォームを生成しているっぽい。

ウィジェット | Django documentation | Django


ソースをみると・・・

radio_option.html

{% include "django/forms/widgets/input_option.html" %}


radio_option.htmlが継承している、input_option.htmlの中身は・・・

input_option.html

{% if widget.wrap_label %}
<label
        {% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}
    {% include "django/forms/widgets/input.html" %}{% if widget.wrap_label %} {{ widget.label }}
</label>
{% endif %}


input_option.htmlが継承している、input.htmlの中身は・・・

input.html

<input 
        type="{{ widget.type }}"
        name="{{ widget.name }}"
        {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}
        {% include "django/forms/widgets/attrs.html" %}
>


どうやら、input.htmlにて ラジオボタンを生成しているっぽいです。

django/django/forms/templates/django/forms/widgets at master · django/django · GitHub


input_option.htmlのlabel内の

inputの上辺りに画像を挿入したらいけるかも・・・?


しかし、RadioSelectでは実現できない。


カスタマイズしたwidgetを生成しよう。


htmlファイルが入った、templateディレクトリの中に、 widgetというディレクトリを生成し、 curry_radio_option.htmlというwidget用のhtmlファイルを作成します。

curry_radio_option.html

{% if widget.wrap_label %}
<label
{% if widget.attrs.id %} for="{{ widget.attrs.id }}"{% endif %}>{% endif %}

<img src="{{ widget.label.image }}">
    <div class="block">
    <input
    type="{{ widget.type }}"
    name="{{ widget.name }}"
    {% if widget.value != None %} value="{{ widget.value|stringformat:'s' }}"{% endif %}
    {% include "django/forms/widgets/attrs.html" %}
>
    {{ widget.label.name }}
        </div>
{% if widget.wrap_label %}
</label>
{% endif %}


cssはこんな感じです。

.block{
    display:block;
    font-size: 20px;
    font-weight: bold;
    align: center;
}

.curry-select{
    display: inline-block;
    padding: 20px;
    margin: 20px;
}


forms.pyの中に、ImageSelectというカスタマイズしたwidgetクラスを作ります。

forms.py

class ImageSelect(forms.widgets.RadioSelect):
    template_name = 'widgets/curry_radio.html'
    option_template_name = 'widgets/curry_radio_option.html'


OrderFormの中のwidget

RadioSelectからImageSelectに変更します。

forms.py

class OrderForm(forms.Form):
    user_name = forms.CharField(label='名前', max_length=20)
    curry = forms.ChoiceField(
        label='カレー',
        choices=generate_curry_choice,
        widget=ImageSelect
    )


無事、画像が入りました〜。


htmlファイルをいじる方法、他のwidgetでも応用できそうです。

他にもっと良い方法があったらぜひ教えてください(>_<)

20181103 ミニTDDBC振り返りその2

mizzsugar.hatenablog.com

の続きです。


今回学んだことは

1. きれいなコミットメッセージの書き方

2. 学習テスト

3. DDDなリファクタリング


この記事では、「2. 学習テスト」について書きます。

今回の題材はこちら

お題: セマンティック・バージョニング · GitHub


我々のGitHub

github.com


2. 学習テスト

問2で、等しいバージョンナンバーから成るSemVerオブジェクトが等しく、異なるバージョンナンバーから成るSemBerオブジェクトが異なるといった実装をしました。

テストコード↓

class TestSemVer(unittest.TestCase):

    def test_同じバージョンを持つ2つのオブジェクトが等しいことを確認(self):
        self.assertTrue(SemVer(1, 4, 2) == SemVer(1, 4, 2))

    def test_違うバージョンを持つ2つのオブジェクトは等しくないことを確認(self):
        self.assertFalse(SemVer(1, 4, 2) == SemVer(2, 30, 400))

このテストを書いた時点のプロダクトコード↓

class SemVer:
     def get_notation(self) -> str:
        return str(self.major) + "." + str(self.minor) + "." + str(self.patch)
     def __eq__(self, other: SemVer) -> bool:
        return True

eqを実装しよう、ということでまず最初の変更↓

    def __eq__(self, other) -> bool:
        return self.get_notation() == other.get_notation() 

しかしこれだと、別クラスのインスタンスであっても、文字列が等しければ等しいとなってしまいます。

ということで、isinstanceを利用しようという話になりました。

しかし、私はisinstanceを利用するのが初めてで使い方がわかりませんでした(^^;


ここで、学習テストの出番です。

学習テストとは、APIや言語の仕様が期待通りに動くかを確認するためのテストです。

ここでは、 isinstanceの引数を色々いれてみて、どのように動くか確かめました。

学習テストのテストコード↓

    def test_isinstanceの勉強(self): # 学習テスト
        self.assertFalse(isinstance('hoge', SemVer)) # 1
        self.assertTrue(isinstance('hoge', str)) # 2

1つ目のテストでは、strオブジェクト'hoge'がSemVerオブジェクトでないかを判定しています。

2つ目のテストでは、strオブジェクト'hoge'がstrオブジェクトであるかを判定しています。

2つともPassしました。


2つのテストから、

1. isinstanceでは、第一引数は判定対象となるオブジェクトであり、第二引数はクラス名を入れる仕様である

2. isinstaceは、第一引数のオブジェクトが第二引数のクラスのインスタンスかを判定するメソッドである

ということが分かりました。


実際に1と2の通りに動くか、確認します。

        self.assertTrue(isinstance(SemVer(1, 4, 2), SemVer))

Passしました。


仕様が分かったところで、プロダクトコードを変更します。

    def __eq__(self, other) -> bool:
        return self.get_notation() == other.get_notation() \
               and isinstance(other, SemVer)

これで、同じバージョンナンバーであり、かつSemVerオブジェクトであれば等しいという実装ができました。


学習テストは、その場ですぐに仕様を確認することが出来るのでとても便利だと思いました!

ドキュメント読んでも理解が曖昧な時に積極的に利用していこうと思います(^^)

isinstanceについてのドキュメント↓

2. 組み込み関数 — Python 3.6.5 ドキュメント

20181103 ミニTDDBC振り返りその1

都内某所でミニTDDBCみたいなものに参加しました。

今回学んだことは

1. きれいなコミットメッセージの書き方

2. 学習テスト

3. DDDなリファクタリング


この記事では、「1. きれいなコミットメッセージの書き方」について書きます。

今回の題材はこちら

お題: セマンティック・バージョニング · GitHub


我々のGitHub

github.com


1. きれいなコミットメッセージの書き方

問1で最初下記のような仮実装をしていました。

import unittest


class SemVer:
    pass
    
    def get_notation(self) -> str:
        return "1.4.2"


class TestSemVer(unittest.TestCase):
    def test_major_minor_patchにそれぞれ142を与えてバージョンオブジェクトを作成(self):
        semver = SemVer(1, 4, 2)
        actual = semver.get_notation()

        expected = "1.4.2"
        self.assertEqual(expected, actual)


if __name__ == "__main__":
    unittest.main()

他の数字でも通るか不安なので、「1.4.2」以外のテストケースを加えました。

    def test_major_minor_patchにそれぞれ230400を与えてバージョンオブジェクトを作成(self):
        self.assertEqual("2.30.400", SemVer(2, 30, 400).get_notation())

テストケースを加えた後、「コミットすっぞ!」ということで、最初下記のようなコミットメッセージを書きました。


「major=2, minor=3, patch=40でSemVerを作るためのテストを追加した」


これに対して、モブメンバーの方から、こんなふうなご指摘が


「コードに書いてること文字に書いてるだけなので、コミットメッセージにする意味ない」


なんと、意味のないコミットメッセージを書いてしまったようです笑


「コミットメッセージは、なぜその変更をしたのか書くもの」


とのこと。

ここで、t-wadaさんの名言を教えてもらいました。

そして、コミットメッセージのリファクタリングが始まりました。

コミットメッセージも仮実装から始めるスタイル?で、

最初とにかく書く→音読する→書いたことを解釈する→コミットメッセージのリファクタリング

という形で実装。


最終的に、

「入力値が異なると通るか不安なので、テストケースを追加した。」

というコミットメッセージになりました!!

これで、パンセン方もニッコリだそうです!!

(2. 学習テスト につづく・・・)

PythonのWEBスクレイピング超絶入門に利用した文法など

PythonでWEBスクレイピングに初挑戦しました!

今回は、下記のnote教材にお世話になりました!

note.mu

超絶入門ということもあり、 さらっと出来てすごいなあと感動しました! (本当にありがたい・・・!)

しかし、私自身、「パーサとは」といったことから分かっていませんでした・・・

このまま次のステップへ進むのは危険なので、

備忘録として、「WEBスクレイピングとは」といったところから

使用したライブラリの簡単な文法まで

書き残したいと思います。

前提

  • Python 3.7.0
  • BeautifulSoup 4.4.0
  • pandas 0.23.4

パーサとは

「パーサとは、構文解析を行うためのプログラムの総称である。」

「パーサの中には、特定の解析対象を明示する形で、HTMLパーサやXMLパーサといった具合に呼称される場合がある」

www.weblio.jp

構文解析とは

「単語や字句で構成される文を、定義された文法に従って解釈し、文の構造を明確にすることである。」

www.weblio.jp

htmlパーサは、解析対象がhtmlとなります。

パーサは、それを読み込むアプリケーションにとって利用しやすい形に変換する機能を持っています。

なお、Pythonの標準ライブラリにhtmlパーサは含まれており、

今回利用するBeautifulSoupはそれをサポートしています。

また、BeautifulSoupは他のサードパーティーのライブラリもサポートしています。

BeautifulSoupで、とあるテキストに記載されているurlを取得してみる

教材にて、hmtl_docという変数に格納されたドキュメントにあるURLを取得して表示させるというものがありました。

 html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""

処理の流れとしてはこんな感じかな、と解釈しました。

①対象となるドキュメントのBeautifulSoupインスタンスの生成

②①で生成したBeautifulSoupインスタンスから、'a'タグの要素を抽出

①対象となるドキュメントのBeautifulSoupオブジェクトの生成

soup = BeautifulSoup(html_doc, 'html.parser') 

BeautifulSoup(対象となるドキュメント, パーサ)

という構文でBeautifulSoupオブジェクトを生成しています。

URLの取得に利用するのは、 BeautifulSoup型の変数soupになります。

また、prettify()という、対象となるBeautifulSoupインスタンスのパーサの型に応じて きれいにフォーマットを整えた文字列に変換して返してくれるメソッドの紹介もありました。

文法は、

BeautifulSoupインスタンス.prettify()

です。

soup = BeautifulSoup(html_doc, 'html.parser')
print(soup) # タグによってインデントがない見にくいドキュメントが表示されます
print(soup.prettify()) # インデントが整えられた、見えやすいドキュメントが表示されます。

aタグの要素を取得して表示してみます。

print(soup.a)
# BeautifulSoupインスタンス.a といったところですかね。

一つの要素しか表示されないかと存じます。

aタグのすべての要素を取得するために、find_all()を利用します。

print(soup.find_all('a'))
# BeautifulSoupインスタンス.find_all('タグ名') 
tags = soup.find_all('a')
for tag in tags:
    print(tag.get('href')

get('href')で、urlのみを取得します。

get(要素)で、その要素の値のみを取得します。

aタグでクラスが"class_name"の要素を抽出するなら↓

soup.find_all("a", {"class", "class_name})
soup.find_all("タグ名", {"class", "クラス名"}) # といったところでしょうか

第一引数がタグ名

第二引数が{str, str}のdict

となります。

requestsで指定されたURLのWEBページのhtmlを取得する

import requests
response = requests.get("https://review-of-my-life.blogspot.com/")
print(response.text)

requestsは、PythonのHTTPを扱うためのライブラリです。

get(url)で、urlにgetリクエストを送った際のレスポンスを取得します。

また、requests.post(url)やrequests.put(url)など、様々なhttpリクエストを送った際のレスポンスも簡単に取得できます。

textでレスポンスの内容をドキュメント化します。

printすると、ドキュメント化されたレスポンスの内容が表示されます。

クイックスタート — requests-docs-ja 1.0.4 documentation

soup = BeautifulSoup(response, 'html.parser')
tags = soup.find_all('a')

for tag in tags:
    print(tag.get('href'))

pandasで分析結果の表を作成する

pandasとは

強力なPython製のデータ分析ツールです。 CSVやhtmlなど様々なファイルからデータを読み込み、DataFrame(表のような形式のオブジェクト)に変換し、様々な形式で出力することができます。

https://docs.pyq.jp/python/pydata/pandas/index.html

Cookbook — pandas 0.23.4 documentation

今回は、表を作成をします。

列を指定してヘッダを作成する

import pandas as pd
columns = ["Name", "Url"]
df = pd.DataFrame(columns=columns) # 列名を指定する
print(df)

DataFrame(column = ["Name", "Url"]) columnはDataFrameのキーワード引数です。

DataFrameオブジェクトに行を追加してボディを作成する

se = pd.Series(['LINEから送った画像を文字起こししてくれるアプリを作るときのメモ①', 'https://review-of-my-life.blogspot.com/2018/03/moji-okosi-1.html'], columns) # 行を作成
df = df.append(se, columns) # データフレームに行を追加
df

Series(シリーズ)は、 1次元の配列のオブジェクト(リストのような形式)となります。

DataFrame(データフレーム)は、 二次元の配列のオブジェクト(表のような形式)となります。

DataFrameオブジェクトにSeriesオブジェクトを追加するのは、

表に行を追加するイメージです。

Intro to Data Structures — pandas 0.23.4 documentation

google.colabでCSVをダウンロードする

from google.colab import files
df1.to_csv("df1.csv")
files.download('df1.csv')

to_csvcsvファイルを作成します。

download(file)でファイルをダウンロードします。

Djangoで画像をクリックしたらモーダルで表示するには

【前提】

方法、見つけました。コピペしたら動きました。

が、コピペ丸にはなりたくないので、 自学のためにこのコードの動きを書くことにしました。

方法、見つけました。コピペしたら動きました。

が、コピペ丸にはなりたくないので、 自学のためにこのコードの動きを書くことにしました。

torina.top

なお、上記サイトではlazyload.jsを読み込んでいますが、 読み込まなくてもうごきました。

lazyloadは、遅延読み込み(不必要な画像の読み込みを後回しにして、画像以外のCSSやJSファイルの読み込みが先に行うためのライブラリ。そうすることで、表示速度を速くすることができる)ためのオプションみたいな感じだから 読み込まなくても動くのでしょうか。

今回は、上記URLとは異なるコードを用いて説明します。

画像の投稿一覧画面にて、

画像をクリックしたらモーダルで大きく表示されるという設定です。

postモデルにfile_pathアトリビュートがあり、各画像のパスを管理しています。

photos.html

    <!-- モーダルウィンドウの中身 -->
    <div class="modal fade" id="imagemodal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
      <div class="modal-dialog modal-lg">
        <div class="modal-content">
          <div class="modal-header">
              <h4 class="modal-title" id="myModalLabel">プレビュー</h4>
              <button type="button" class="close" data-dismiss="modal"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
          </div>
          <div class="modal-body">
            <img src="" id="imagepreview" class="img-responsive" >
          </div>
          <div class="modal-footer">
            <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
          </div>
        </div>
      </div>
    </div>


    <!-- 画像一覧 -->
    <div style="padding-bottom:200px">
        <ul class="one-post">
          {% load static %}
            {% for post in posts %}
            <li><div class="user-page-img modal-open"><a href="#"><img src="{% static post.file_path %}" class="center" onclick="pop(this)"></a></div></li> <!--  file_pathはpostモデルで管理している、画像のパス-->
            {% endfor %}
        </ul>
    </div>

onclick="pop(this)

onclick=""で、その要素をクリックした際の関数呼び出しをしています。

pop(this)がどのような処理を行うかは、jsに記載されています。

function pop(self) {
    $('#imagepreview').attr('src', $(self).attr('src'));
    $('#imagemodal').modal('show');
}

selfが引数となっています。

html上では、実際に利用されるオブジェクトとして thisが利用されています。

JSでは、カレントオブジェクト()を参照するためにthisが使われます。

ここでは、imgにonclickが登録されているので、imgになりますね。

複数のimgが表示されていますが、クリックしたimgを参照します。

JavaPythonでは、this(もしくはself)といえば、インスタンス自身を指すので、やはりJSは難しいですね)

developer.mozilla.org

①でidがimagepreviewである要素にattrを実行しています。

attr(a, b)でaという要素の属性をbに設定しています。

attr(a)で、aという要素を取得しています。

imagepreviewのsrcの値を、クリックしたimgのsrcの値に設定しています。

①が終わったら、②でモーダルを表示しています。

Python3で再帰処理を書いたよ

再帰処理を1日中学んだ翌日、夢の中で再帰処理を書いてしまうほどになってしまったので

これはお告げだと思い、ブログに書くことにしました。

【前提】

再帰処理とは

「プログラムのある関数の中から自分自身の関数を呼び出す」処理のことらしい

(「呼び出し」と書いてあるけど、やっていることは同じなので「処理」置き換えてもいいかな、と判断しました。あと、色々読んだ中でこの説明が一番しっくりきた)

www.weblio.jp

と言ってもイメージが沸かないので、実際に再帰処理を用いた処理を書きます。

リストに格納されている文字列の登場回数を数える関数です。 (collectionsのCounterで間に合うやつです汗)

count_name.py

from typing import Dict, List


l = ['a', 'b', 'a']



def count_name(name: str, list: List[str], num: int) -> int:
    if not list: # ①listが空の場合(listが[ ] の場合は、falseを返す)
        return num
    if list[0] == name:  # ②listの先頭がnameと一致する場合
        num += 1
    return count_name(name, list[1:], num) # ③listは先頭を1つずらしたものにし、次の再帰処理へ

print(count_name('a', l, 0)) # ④

こんな感じで処理が行われています。

1.list = ['a', 'b', 'a'], name = 'a'

①list == [ ] ではないのでスキップ

②list[0] == 'a'なので

num += 1

③return count_name('a' , list['b', 'a'])

2.list = ['b', 'a'], name = 'a'

①list == [ ] ではないのでスキップ

②list[0] = 'b'なのでスキップ

③return count_name('a' , list['a'])

3.list = ['a'], name = 'a'

①list == [ ] ではないのでスキップ

②list[0] == 'a'なので

num += 1

③return count_name('a' , list[ ])

4.list = [ ], name = 'a'

①list == [ ] なので retun num

③のcount_nameを繰り返した結果、2が返ってきています。 ④でcount_nameをprintすると、count_name内で呼び出しているcount_nameの返り値が2なので、 2がプリントされます。

ちなみに、③は「count_name(list[1:], name 」では駄目なのか? と私は思いました。 駄目です。

理由は、return がないと何も実行されず、処理が終わらないからです。

関数では、returnの箇所で処理が終了するのですが、それがないと

ifで当てはまらなかった場合に永遠に何も起こらないことになってしまいます。

おまけ。後から提案されたやつ↓

def count_name(name: str, list: List[str]) -> int:
    if not list:
        return 0
    
    head, *tail = list
    n = 1 if head == name else 0
    return n + count_name(name, tail)

print(count_name('a', l))

head, *tail = list 以下はこんな感じです。

head = list[0]
tail = list[1:]

if head == name:
    n = 1
else:
    n = 0
return n + count_name(name, tail)

それにしても、自分が書いたやつ、もっさりしてる(涙)

私のがもっさりスパゲッティなら、後者のコードはそうめんなので

私もそうめん書けるようにがんばります。

PyConに初めて参加してみて学んだこと

初めてPyConに参加しましたので、参加したセッションで学んだことをまとめました! 普段考えなくても動くようなプログラムの裏側を学ぶことができて、 よりたくさんのことができるようにするための糧となりました^^

何より、PyConで未経験から転職したというLTが最も勇気づけられて・・・ 私もPyConでエンジニア転職したい!!!!!!!(本気です!笑)


●印象に残ったセッションと学んだこと●

-あなたとPython今すぐパッケージン

-トイル撲滅記

-Pythonで学ぶUnixプロセス


1日目

あなたとPython今すぐパッケージン(pipenvの話)

今まで何気なくpipを使っていたけれども、pipの仕組みを学ぶきっかけになりました。

(あとから調べた) pipとは パッケージイントールするためのツール。パッケージは、PyPIPython Package Index)というPythonパッケージを管理する団体で管理しているものを利用する。

virtualenvとは

パッケージを切り分ける仮想環境。 プロジェクトごとにパッケージのバージョンを依存関係を切り替えるために構築する。

pipfileとは

依存ライブラリのフォーマット。 requirement.txtの置き換え。

pipenv登場前:virtualenvを構築後、pipでvirtualenvにパッケージをインストール ↓

pipenvたるものが登場

※pipenvとは、「pip+virtualenv+pipfileを包括的に取り扱うツール」

pipenv:「pip install pipenv」のコマンドだけで環境構築もパッケージインストールもできる

(けど遅い)

疑問:個人的には、pipenvじゃなくてrequirement.txtでも間に合うのですが、

pipenvの方が良いのは具体的にはどんな開発をするとき?


2日目

トイル撲滅記

普段保守運用で課題感を感じているからか、やはりSRE好きだなと思いました。

「SREはトイル撲滅がメイン」

トイルとは・・・「本番サービスに関する作業で手作業で繰り返し行われ自動化することが可能であり戦術的で長期的な価値を持たず作業量がサービスの成長に比例する」


Case

環境構築作業(職人技になっている、機能開発開始までに時間がかかる)

ー>Docker Compose

Docker-composeのみで開発できるようになるし、環境差異によるバグがおさえられる (やりたい!!!)


Case

デプロイ作業(手順書作成ー>1代ずつ縮退・更新、チェッカーが必要)

ー>Amazon ECSの利用

コンテナオーケストレーション、サーバー管理不要、スケジュール不要


ー>CI/CDの利用

※CI(Continuous Intergration) / CD(Continuous Delivery, Deploy)

プッシュするとー>Unitテストー>Flake8ー>Radon(コードの複雑さを図るツール)

走る。

レビューの手間が省ける。 書き方が統一化されて保守しやすくなる。 ( やりたい!!!)


Case 管理サーバにログインできない。ログを見てわかる状態が望ましい。

Effective Python->Noneを返すよりもExceptionを返そう

Exceptionでloggingするべき。

特別な例外を出すことでやりたいことの明確化。


AWS X-Ray(setting.pyに設定する)

・リクエストに関するデータ収集

SQLがどれだけ発行されているかもわかる

非エンジニアでも調査できるようになった。 (素晴らしい!)


Pythonで学ぶUnixプロセス

普段何気なく使っていたbashの仕組みを学べて良かった。

大前提:すべてのプログラムはプロセスによって動いている。

プログラムの実行時にプロセスも生まれ、終了したらプロセスも死ぬ


システムコール

アプリケーションプログラムに対して提供する機能を呼び出すこと。

プログラムが動作するときの2つの処理空間

カーネルとユーザーランド

カーネル:ハードウェアとやりとり

ユーザーランド:カーネルとやり取りする空間。ユーザーランドの仕組みをシステムコールという


プロセス:

あらゆるコードはプロセスで実行される

プログラムの実行時にプロセスも生まれ、終了したらプロセスも死ぬ

プロセスにはIDがある

省略されてpidと呼ばれる

os.getpid()で現在実行しているプロセスを確認できる


プロセスにはリソースの制限がある

カーネルによって1プロセス毎にリソースが制限される

(本題)プロセスは子プロセスを作れる:

fork(2)システムコールで子プロセスを作れる

fork(2)は親プロセスのコピー

fork(2)からifが分岐する

親が死んでも子は生き残る。

生き残った子プロセスを孤児プロセスという

親プロセスは、子プロセスが終わるまで待てる

os.wait()でできる


ゾンビプロセス:

子プロセスのほうが先に終了

親プロセスはwaitで子プロセスの終了ステータスを要求しない

孤児とゾンビのち外

先に自分が死んでwait待ちなのがゾンビ


デーモンプロセス:

端末から制御されるのではなく、バックグラウンドから制御

デーモン化はプロセスを完全に制御端末やシグナルから切り離せる