Markdwon形式のメモ管理(複数管理 第五回)

データ管理

「Markdwon形式のメモ管理」の第五回目です。

今回で、Markdwon形式のメモ管理の機能は作り終えたいと思います。

今回はデータの登録、更新について紹介します。

前回の内容は以下を参照してください。

Markdwon形式のメモ管理(複数管理 第四回)

今回の手順は以下のとおりです。

Flaskのapp.py改修

Flaskのapp.pyを改修します。

コードは以下のとおりです。

    
import os

from flask import Flask, request
from flask_cors import CORS

import pymysql.cursors
import json

RESULT_CODE_SUCESS = 'sucess'

app = Flask(__name__)
CORS(app)


def db_connection():
    conn = pymysql.connect(
        host='db',
        port=3306,
        user=os.environ.get('MYSQL_USER'),
        password=os.environ.get('MYSQL_PASSWORD'),
        db=os.environ.get('MYSQL_DATABASE'),
        cursorclass=pymysql.cursors.DictCursor
    )

    return conn


@app.route("/", methods=["GET"])
def test():
    return "Hello World"


RESULT_CODE_SUCESS


@app.route('/mariadb')
def mariadb():
    conn = db_connection()
    with conn.cursor() as cur:
        sql = "SELECT * FROM user"
        cur.execute(sql)
        results = cur.fetchall()

    return results[0]


@app.route('/markdown', methods=["POST"])
def post_markdown():
    url = request.json["url"]
    title = request.json["title"]
    body = request.json["body"]

    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(
            f"INSERT INTO markdown (url, title, body) values('{url}','{title}','{body}')")
        conn.commit()

    return RESULT_CODE_SUCESS


@app.route('/markdown<path:path>', methods=["PUT"])
def put_markdown(path):
    title = request.json["title"]
    body = request.json["body"]

    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(
            f"UPDATE markdown SET title = '{title}', body = '{body}' WHERE url = '{path}' ")
        conn.commit()

    return RESULT_CODE_SUCESS


@app.route('/markdown', methods=["GET"])
def get_markdown():
    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(f"SELECT * FROM test.markdown")
        results = cur.fetchall()

    return json.dumps(results, indent=2)


@app.route('/markdown/<path:path>', methods=["GET"])
def get_markdown_url(path):
    conn = db_connection()
    with conn.cursor() as cur:
        cur.execute(f"SELECT * FROM markdown WHERE url = '{path}'")
        results = cur.fetchall()

    if not results:
        return json.dumps({}, indent=2)
    
    return json.dumps(results[0], indent=2)

    
  

db_connectionの関数を用意しました。

conn.cursorでwithを使うようにしました。 withは処理終了時にconnを暗黙的にcloseします。

ファイル操作によく使用されます。

post_markdownとput_markdownがメインの処理になります。

それぞれのAPIは以下のようになります。

Markdownデータ登録用API

URL /markdown
メソッド POST
フォーマット JSON
パラメータ {'url': 'test/test1', 'title' : 'TEST', 'body' : '# TEST'}

Markdownデータ更新用API

URL /markdown/{url}
メソッド PUT
フォーマット JSON
パラメータ {'url': 'test/test1', 'title' : 'TEST', 'body' : '# TEST'}

更新はurlの内容と一致するデータを更新します。

SubHeaderNavigation改修

サブヘッダーナビゲーションに登録、更新用のボタンを配置します。

    
import { useEffect } from "react";

import axios from "axios";

import {
  BottomNavigation,
  BottomNavigationAction,
  Box,
  TextField,
  Toolbar,
  Typography,
} from "@mui/material";

import EditIcon from '@mui/icons-material/Edit';
import PageviewIcon from '@mui/icons-material/Pageview';
import SaveIcon from '@mui/icons-material/Save';

export default function SubHeaderNavigation(props) {

  const { markdown, setMarkdown, editMode, onClickMode } = props;

  const onClickSaveMarkdown = () => {
    axios.post(`http://localhost:5000/markdown`, markdown)
      .then(res => {
        console.log(res.data);
      })
  };

  const onClickUpdateMarkdown = () => {
    axios.put(`http://localhost:5000/markdown` + markdown.url, markdown)
      .then(res => {
        console.log(res.data);
      })
  };

  const onChangeURL = (event) => {

    markdown.url = event.target.value;
    setMarkdown(
      {
        url: event.target.value,
        title: markdown.title,
        body: markdown.body
      });
  };

  const onChangeTitle = (event) => {
    setMarkdown(
      {
        url: markdown.url,
        title: event.target.value,
        body: markdown.body
      });
  };

  useEffect(() => {
  }, [markdown.url, markdown.title]);

  return (
    <Box>
      <Toolbar sx={{ backgroundColor: "#1976d2", display: "flex", justifyContent: "space-between" }}>
        {editMode ? <TextField label="URL" value={markdown.url} onChange={onChangeURL}></TextField> : <Typography>{markdown.url}</Typography>}
        {editMode ? <TextField label="TITLE" value={markdown.title} onChange={onChangeTitle}></TextField> : <Typography>{markdown.title}</Typography>}
        <BottomNavigation showLabels sx={{ backgroundColor: "#1976d2" }}>
          {editMode ?
            [<BottomNavigationAction label="表示" icon={<PageviewIcon />} onClick={onClickMode} />,
            <BottomNavigationAction label="新規登録" icon={<SaveIcon />} onClick={onClickSaveMarkdown} />,
            <BottomNavigationAction label="更新" icon={<SaveIcon />} onClick={onClickUpdateMarkdown} />].map((component) => component)
            :
            <BottomNavigationAction label="編集" icon={<EditIcon />} onClick={onClickMode} />
          }
        </BottomNavigation>
      </Toolbar>
    </Box>
  );
}
    
  

新規登録と更新ボタンを配置しました。

今後見直しは必要ですが、まずは、動作できるものをつくるために簡易的に実装しています。

各ボタンをクリックしたときのAPIの呼び出しも行っています。

こちらも登録、更新成功時のメッセージについて今後検討を行います。

Page1Page改修

Page1Pageを改修します。

こちらは、DBに登録されていないデータを取得したときに、新規登録の編集画面に遷移するような改修になります。

    
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import axios from "axios";

import { createTheme, ThemeProvider } from "@mui/material";

import MarkDownTemplate from "../template/MarkdownTemplate";
import EditTemplate from "../template/EditTemplate";

export default function Page1Page() {

  const darkTheme = createTheme({
    palette: {
      mode: "dark",
      primary: {
        main: "#1976d2"
      }
    }
  });

  const [markdown, setMarkdown] = useState([]);
  const [editMode, setEditMode] = useState(false);

  const location = useLocation();

  useEffect(() => {
    axios.get("http://localhost:5000/markdown" + location.pathname)
      .then(res => {
        if (!Object.keys(res.data).length) {
          setMarkdown(
            {
              url: location.pathname,
              title: '',
              body: ''
            });
          setEditMode(true);
        } else {
          setMarkdown(res.data);
        }
      })
  }, [])

  const onClickMode = () => { setEditMode(!editMode) }

  console.log('MODE' + editMode);
  return (
    <ThemeProvider theme={darkTheme}>
      {editMode ?
        <EditTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} onClickMode={onClickMode} /> :
        <MarkDownTemplate markdown={markdown} setMarkdown={setMarkdown} editMode={editMode} onClickMode={onClickMode} />}
    </ThemeProvider>
  );
}
    
  

データ取得APIを使ってデータを取得した際に、データがないときに、useStateのsetMarkdownに空のデータをセットします。

ただし、urlはurlパスをそのまま設定しています。

また、useStateのsetEditModeにtrue(編集モード)を設定します。

    
・・・略・・・
  useEffect(() => {
    axios.get("http://localhost:5000/markdown" + location.pathname)
      .then(res => {
        if (!Object.keys(res.data).length) {
          setMarkdown(
            {
              url: location.pathname,
              title: '',
              body: ''
            });
          setEditMode(true);
        } else {
          setMarkdown(res.data);
        }
      })
  }, [])
・・・略・・・
    
  

画面確認

docker-compose upを使って各コンテナを起動します。

docker-compose.ymlファイルがある場所で以下のコマンドを実行します。

    
docker-compose up
    
  

起動したら以下のURLをブラウザに入力します。

http://localhost:3000

以下ブラウザの表示です。

編集画面

テキストを修正することができます。また、登録、更新ボタンを押下できます。

markdown-view

urlが存在しない場合も編集画面が表示されて、urlの入力ができるようになりました。

markdown-view

まとめ

編集画面のデータ登録、更新をおこなえるようにしました。

細かい処理はのこっていますが、Markdownのデータの複数データ管理ができるようになりました。

コメント

0 件のコメント:

コメントを投稿

コメントをお待ちしています。