顧客管理ソフトの作成 – 第4回

はじめに

今回はCRUD処理のひつのRead、顧客情報を一覧と詳細画面に表示する機能を作成していく。

今回やること

  • 前回作成したデータベースの情報を一覧に表示する
  • 上記の情報を詳細画面に表示する
  • 一覧画面と詳細画面の画面遷移を作成する

顧客一覧を表示

顧客情報をdatabaseから取得

まず、app_layout.pyを下記のように修正して一覧画面を読み込むようにする。

ClientDetail → ClientManagementApp

app_layout.py

def _load_main_content(self, menu_id):
        """メニューIDに応じたメインコンテンツを読み込む"""
        if menu_id == "clients":
            return ClientManagementApp(self.page) # ClientDetailから修正

client_app.pyに下記のコードを追加。

client_app.py

import flet as ft
from database import SessionLocal # 追加
from models.client import Client # 追加

def _create_layout(self):
…

def _get_clients(self): # 追加
        """データベースから得意先一覧を取得"""
        db = SessionLocal()
        try:
            query = db.query(Client)
            client = query.all()
            return client
        finally:
            db.close()

client_app.pyの初期化処理の最後に下記のコードを追加してテスト。

client_app.py

clients = self._get_clients()
print(clients)

ターミナルに下記のように表示されれば成功。

terminal

[
 <Client(id=1, client_cd='0001', client_name='山田商事株式会社')>,
 <Client(id=2, client_cd='0002', client_name='山田商事株式会社')>,
 <Client(id=3, client_cd='0003', client_name='佐藤電機株式会社')>,
 <Client(id=4, client_cd='0004', client_name='田中物産株式会社')>,
 <Client(id=5, client_cd='0005', client_name='高橋建設株式会社')>
]

client.pyにdef __repr__関数があるため、上記のようにわかりやすく表示されている。この関数がないと下記のようになってわかりにくい。

[
 <models.client.Client object at 0x000002B664BEF230>,
 <models.client.Client object at 0x000002B664BDB750>,
 <models.client.Client object at 0x000002B664BDB890>,
 <models.client.Client object at 0x000002B664BA6C40>,
 <models.client.Client object at 0x000002B664BA6D70>
]

取得した顧客情報をテーブルに表示

client_app.pyに下記のコードを追記

client_app.py

def _get_clients(self):
…

def _refresh_table(self): # 追加
        """テーブルを更新する"""
        self.data_table.rows.clear()
        clients = self._get_clients()
        for client in clients:
            self.data_table.rows.append(
                ft.DataRow(
                    cells=[
                        ft.DataCell(
                            ft.Text(
                                client.client_cd,
                                selectable=True,
                            )
                        ),
                        ft.DataCell(
                            ft.Text(
                                client.client_name,
                                selectable=True,
                            )
                        ),
                        ft.DataCell(
                            ft.Text(
                                client.client_name_phonetic,
                                selectable=True,
                            )
                        ),
                        ft.DataCell(
                            ft.Text(
                                client.client_name_alias,
                                selectable=True,
                            )
                        ),
                        ft.DataCell(
                            ft.ElevatedButton(
                                "詳細",
                                on_click="hoge",
                                style=ft.ButtonStyle(
                                    shape=ft.RoundedRectangleBorder(radius=5)
                                ),
                            )
                        ),
                    ]
                )
            )
        self.page.update()

self.page.update()について

fletでは、画面の内容を書き換えた場合はpage.updateしないと表示が変わらない。しかし、この時点ではpage.add()をしていないので、このコードがなくても正常に動く。

_refresh_table()は、今後検索による絞り込みや、詳細画面から一覧画面への遷移などでpage.add()をした後にも使用する関数なのでpage.update()を入れている。

client_app.pyのdef _create_main_contentの最後に下記のコード追加して、初期データを表示する。

client_app.py

# 初期データ表示
self._refresh_table()

下記のように顧客情報が表示されれば成功。

初期化処理に追加していたテスト用の下記のコードは不要なので削除。

client_app.py

clients = self._get_clients() 
print(clients)

def _create_main_content(self)の仮データのrowsは不要なので下記のように修正。

client_app.py

rows=[]

画面遷移の作成

詳細画面の表示に行く前に、一覧画面から詳細画面への遷移を作成する。

main.pyのdef mainの中に下記のコードを追加

main.py

def main(page: ft.Page):
  def route_change(e):
          """ルート変更時の処理"""
          # 現在のビューをクリア
          page.views.clear()

          # ルートに応じて画面を表示
          if page.route == "/client/list":
              page.views.append(
                  ft.View(
                      "/client/list",
                      [AppLayout(page, initial_menu="clients")],
                  )
              )

          page.update()

 # イベントリスナーの登録 route_changeの後に()は付けない。
  # ()をつけるとすぐに実行されてしまう。
  page.on_route_change = route_change
  # 初期ルートを設定して画面を表示
 # これにより on_route_change が発火し、route_change(e) が呼ばれる。
  page.go("/client/list")

下記は不要になったのでコメントアウト

mian.py

# layout = AppLayout(page, initial_menu="clients")
# page.add(layout)

実行して顧客一覧が表示されれば成功。

次に詳細画面への遷移を設定する。

mian.py

import flet as ft
…

from database import init_db
from client_detail import ClientDetail # 追加

if page.route == "/client/list":
…

elif page.route.startswith("/client/detail"): # 追加
  # 詳細画面(/detail/123 のような形式)
  # 負のインデックス(-1)を使用すれば階層が深くなってもて末尾の値を取得できる
  client_id = int(page.route.split("/")[-1])
  page.views.append(
    ft.View(f"/client/detail/{client_id}", 
    [ClientDetail(page, client_id)])
  )

この追加したルートを実行するために、一覧の詳細ボタンにイベントを登録する。まずは、詳細ボタンのon_clickにself._on_detail_click(client.id)を登録。

client_app.py

def _refresh_table(self):
…

ft.DataCell(
  ft.ElevatedButton(
    "詳細",
    on_click=self._on_detail_click(client.id),
    style=ft.ButtonStyle(
      shape=ft.RoundedRectangleBorder(radius=5)
    ),
  )
),

次に、その関数の中身を下記のように作成にする。

client_app.py

def _refresh_table(self):
…

def _on_detail_click(self, client_id): # 追加
  """詳細ボタンクリック時の処理"""

  def handler(e):
    self.page.go(f"/client/detail/{client_id}")

    return handler

client_detail.pyのdef __init__()の引数にclient_idを追加。実行して詳細が面が表示されれば成功。この時点では、どのボタンをクリックしても同じ空の画面が表示されるので追加したclient_idに対応して情報を表示するように修正する。

まずは、client_idを受け取れるようにclient_detail.pyのinitを下記のように修正。

client_detail.py

def __init__(self, page, client_id):

データベースから情報を取得するために下記のimportを追加。

client_detail.py

import flet as ft
…

from database import SessionLocal
from models.client import Client

initに下記のコード追加。

client_detail.py

def __init__(self, page, client_id):
…

self.client = None
self.client_id = client_id

1件の顧客情報を取得する関数を追加。

client_detail.py

def _load_client(self):
  """データベースから1件の顧客情報を取得"""
  db = SessionLocal()
  try:
    self.client = db.query(Client).filter(Client.id == self.client_id).first()            
  finally:
    db.close()

initに下記のコード追加してテスト。選択した顧客の情報が表示されたら成功。

client_detail.py

def __init__(self, page, client_id):
…

self._load_client()
print(self.client)

def _create_main_content(self):の各項目に値を設定していく

client_detail.py

def _create_main_content(self):
…
ft.Column(
  controls=[
    ft.TextField(
      label="顧客CD",
      width=400,
      value=self.client.client_cd,
    ),
    ft.TextField(
      label="顧客名",
      width=400,
      value=self.client.client_name,
    ),
    ft.TextField(
      label="ふりがな",
      width=400,
      value=self.client.client_name_phonetic,
    ),
    ft.TextField(
      label="別名",
      width=400,
      value=self.client.client_name_alias,
    ),
    ft.TextField(
      label="代表者",
      width=400,
      value=self.client.president,
    ),
    ft.TextField(
      label="与信枠",
      width=400,
      value=self.client.credit,
    ),
    ft.TextField(
      label="与信枠登録日",
      width=400,
      value=self.client.credit_reg_date,
    ),
 ]
),
# 右側の列
ft.Column(
  controls=[
    ft.TextField(
      label="郵便番号",
      width=400,
      value=self.client.postal_code,
    ),
    ft.TextField(
      label="住所",
      width=400,
      value=self.client.address,
    ),
    ft.TextField(
      label="TEL",
      width=400,
      value=self.client.phone,
    ),
    ft.TextField(
      label="FAX",
      width=400,
      value=self.client.fax,
    ),
    ft.TextField(
      label="備考",
      width=400,
      multiline=True,
      min_lines=3,
      max_lines=5,
      value=self.client.notes,
    ),
  ]
),

self._load_client()を初期化処理の頭に変更する。下記の画像のように各顧客のデータが表示されれば成功。

client_detail.py

# 初期化処理
self._load_client()

最後に一覧画面に戻る処理を追加する。client_detail.pyに下記の関数を追加。

client_detail.py

def _on_back_click(self, e):
  """ "一覧画面に戻るボタンのクリック処理"""
  self.page.go("/client/list")

上記の関数を、self.back_to_listボタンに設定する。顧客詳細の左矢印をクリックして顧客一覧に戻れたら成功。

client_detail.py

def _create_header(self):
…
self.back_to_list = ft.IconButton(
  tooltip="一覧に戻る",
  icon=ft.Icons.ARROW_BACK,
  on_click=self._on_back_click, # 関数を指定
  style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=5)),
)