PUROGU LADESU

ポエムがメインのブログです。

【Kotlin】Gradleのdependanciesを反映させる方法

IntelliJ IDEA初心者です。

問題

build.gradle.ktsのdependanciesにarrowを追加したがimportが認識しない。
ビルドしなおしてもダメ。
ChatGPTに教えてもらった。

方法

画面右上の歯車マークの下のベルマークの下の謎アイコンをクリックする。
(よく見るとゾウさんマーク?)
Gradleのタブが開く。
一番左の更新ボタンを押すとリロードされ反映される。

Intellij IDEA

【Python】メソッド内でのクラス変数の注意点

インスタンスメソッドからクラス変数を利用する

クラス変数はインスタンスメソッドから利用する場合混乱しやすいため注意が必要。

selfでアクセスする場合、インスタンス変数 -> クラス変数の順で探しに行くため、
クラス変数しかない状態でselfで代入すると、インスタンス変数が作成されてしまい、
以降selfではインスタンス変数にアクセスすることになってしまう。

class Person(object):
    # クラス変数
    count = 1

    def __init__(self, age=8):
        # インスタンス変数
        self.__age = age
    
    def add_count(self):
        # 参照は構わないが、ややこしい可能性あり
        print(self.count)
        # これをするとインスタンス変数になってしまう
        self.count += 1
        # 以下のどれかにする
        Person.count += 1
        # __class__.count += 1
        # type(self).count += 1
        print(self.count)
        print(Person.count)

    # クラスメソッド(clsでクラス変数にアクセス)
    @classmethod
    def get_cls_count(cls):
        return cls.count


これを避けるには以下のどれかの方法でクラス変数にアクセス(更新)する。
参照だけならselfでも問題ないが、可読性や取り違え防止を考えると参照時も統一したほうが良いかもしれない。
見た目のわかりやすさから言うと__class__がいいように思う。

Person.count += 1
__class__.count += 1
type(self).count += 1


[Python入門]クラス変数/クラスメソッド/スタティックメソッド:Python入門(1/2 ページ) - @IT

クラスメソッド内からクラス変数にアクセスする

クラスメソッドからはclsでアクセスする。
ちなみにclsは慣例であって別の名前でも問題ない。

そもそもの話として

そもそもインスタンスメソッドからクラス変数をアレコレ更新する、というのがそもそも設計として良くないとは思われるが、
使いたくなる場面は出てくるだろう。

【Django】ログインを強制する

LoginRequiredMixin

ログインしていることを前提とした機能の場合、
強制的にログイン画面に飛ばしたい。

クラスベースではLoginRequiredMixinを実装することで可能となる。
ログイン後は、指定した画面に遷移する。
デフォルトではアクセスしようとした画面に戻してくれる。

from django.contrib.auth.mixins import LoginRequiredMixin

class HomeView(LoginRequiredMixin, ListView):

このMixinは一番左に記述する必要がある。
遷移先はsettings.pyの下記で設定が可能。
ない場合はデフォルトが適用される。

LOGIN_URL = "accounts:login"

UserPassesTestMixin

チェック関数を自作して条件を通過した場合にアクセスさせる

PermissionRequiredMixin

機能ごとに指定した権限を持つユーザのみアクセスさせる


Djangoの認証システムを使用する | Django ドキュメント | Django

【Django】モデルインスタンスのsaveとバリデーション

Formクラスにもバリデーションを定義することができるが、
Modelクラスにも定義することができる。

full_clean()

バリデーションを実施するにはfull_clean()を呼べばよい。
full_clean()では下記が呼ばれる。
clean_field() -> 各フィールドのvalidatorsに定義したものが呼ばれる
clean() -> フィールドに関連しない全体のチェック。cleanを定義していなければ何もしない。
validate_unique() -> フィールドに定義したunique制約をチェックする
validate_constraints() -> Meta.constraintsに定義したデータベース制約をチェックする

save()では呼ばれないのでその前で呼ぶ。
問題があった場合はValidationErrorをキャッチする。

"""インスタンスの保存処理"""
try:
    sample = Sample()
    sample.full_clean()
    sample.save()

except ValidationError as e:
    print(e)
except DatabaseError as e:
    print(e)
"""モデルクラス"""
def clean(self) -> None:
        """フィールドに関連づかないバリデーションエラー"""
        something_happen = True
        if something_happen:
            raise ValidationError("何らかのエラー!!")

save()で呼ぶにはオーバーライドする。

"""モデルクラス"""
    def save(self, force_insert, force_update, using, update_fields):
        """saveの前になにかさせる場合"""
        self.full_clean()
        return super().save(force_insert, force_update, using, update_fields)
    

is_valid()

そもそもFormクラスのis_valid()を呼ぶことでこいつが呼ばれる思想なのかもしれない。
Formのis_valid() -> full_clean() -> _post_clean() -> Modelのfull_clean()
この順番で呼ばれてくるようだ。

モデルインスタンスリファレンス | Django ドキュメント | Django

【Django】JSONを返すだけのクラスベースView

画面がいらない場合、テンプレートを使わない場合はクラスベースではViewを使用します。
HttpResponseを作ってcontentにセットして返せば良し。
他のメソッドが必要なら、def post, def headなどを定義すれば良し。

from django.views.generic import View

class JsonTestView(View):
    """テンプレート使わない"""
    def get(self, request, *args, **kwargs) -> HttpResponse:
        response = HttpResponse()
        response.content = self.create_json()
        response.headers["Content-Type"] = "text/json"
        return response
    
    def create_json(self):
        data = {
            "id": 1,
            "name": "ジャック",
            "age": 25,
        }
        return json.dumps(data)

【Django】素のSQL文の実行

パターンは2つある

1.モデルオブジェクトに紐づける(SELECTの場合)
2.モデルに関係ないSQLを実行する(INSERT, UPDATE, DELETE, モデル生成しないSELECT)

1.モデルオブジェクトに紐づける

detail = DailyReportDetail.objects.raw("SELECT * FROM reportapp_dailyreportdetail WHERE id = %s", [6])[0]
detail.save()

結果はモデルオブジェクトになるので、モデルに合ったテーブルを指定している必要がある。

モデルを使うなら、主キーは必ずSELECTに含める(ないとエラー)
SELECTにないフィールドを参照したら、その時点で追加のクエリが発行される

2.モデルに関係ないSQLを実行する

INSERT, UPDATE, DELETEはこちら。
SELECTでモデルじゃなく単なる値を取りたい場合もこちら。

from django.db import connection
with connection.cursor() as cursor:
    cursor.execute("UPDATE myapp_person SET flag = 1 WHERE last_name = %s", [name])

with connection.cursor() as cursor:
    cursor.execute("SELECT flag FROM myapp_person WHERE last_name = %s", [name])
    row = cursor.fetchone() タプルで返される
    row = cursor.fetchall() 複数結果、タプルのリスト
    cursor.description タプルの1個目がフィールド名みたい -> [x[0] for x in cursor.description]

fetchone(), fetchall() はレコードをリストで返してくる。名前はつかない。
名前をつけたい場合はnamedtupleを使う。
descriptionを渡してフィールド名を定義し、レコード配列を渡す。

nt_res = namedtuple("Result", [x[0] for x in cursor.description])
res = nt_res(*row)

素の SQL 文の実行 | Django ドキュメント | Django

【Django】標準ログイン画面を使う方法

Djangoにはログイン画面を実装する方法が提供されていますので、
それを使用します。

Djangoの認証システムを使用する | Django ドキュメント | Django

ルーティングの追加

プロジェクトルートのurls.py

urlpatterns = [
    path("accounts/", include("django.contrib.auth.urls")),
    

accounts/login でアクセスできるようになります。
この一行で他にもlogout, password_changeなどが利用可能になります。
カスタマイズする場合はaccounts/loginなどそれぞれ定義します。

htmlテンプレート

loginはテンプレートを自作する必要があります。
テンプレートはregistrationフォルダに入れる必要があります。

他の機能は下記のフォルダに入っている管理画面のテンプレートが使われるようです。
login.htmlはadminの方に入ってるので自作しないといけないです。
.venv/lib/python3.10/site-packages/django/contrib/admin/templates/registration

マニュアルにサンプルが載ってます。
コンテキストのリストにはないが画面内でuserが使える。

<h2>ログイン</h2>

{% if form.errors %}
<p>正しいユーザとパスワードを入れてください</p>
{% endif %}

{% if next %}
  {% if user.is_authenticated %}
  <p>このユーザー({{ user }})はページを見る権限がありません。別のユーザでログインしてください。</p>
  {% else %}
  <p>ページを見るにはログインしてください {{ user }}</p>
  {% endif %}
{% endif %}

<form action="{% url 'login' %}" method="post">
  {% csrf_token %}
  {{ form.as_p }}

  <input type="submit" value="login">
  <input type="hidden" name="next" value="{{ next }}">
</form>

<p><a href="{% url 'password_reset' %}">パスワード忘れた人</a></p>

ログイン画面の設定

settings.py

ログイン画面の場所、ログイン後に遷移する場所の設定。

LOGIN_URL = "login"
LOGIN_REDIRECT_URL = "/admin/"