はじめに
お久しぶりです。Tomita Kentaroです。今回は、過去に作ったFlask(python)とReact(JavaScript)で作ったToDoリストを紹介したいと考えております。Flaskをサーバーとして、Reactをフロントとして動かしてあります。
Flask(バックエンド)の紹介
まずは、ディレクトリ構造の紹介です。
.
├── TodoDB.db
├── main.py
├── mecab-test
│ └── ...
├── requirements.txt
└── utils.py
続いて、main.pyの紹介です。
使用できる、ユーザは "a" , "b" , "c" の3人を用意しました。
from flask import Flask
from flask import request, make_response, jsonify
from flask_httpauth import HTTPBasicAuth
from flask_cors import CORS
import sqlite3
import webbrowser
app = Flask(__name__, static_folder="./build/static", template_folder="./build")
auth = HTTPBasicAuth()
CORS(app, supports_credentials=True)
users = {
"a":"1",
"b":"2",
"c":"3"
}
@auth.get_password
def get_pw(username):
if username in users:
return users.get(username)
return None
@app.route('/')
@auth.login_required
def index():
webbrowser.open('http://127.0.0.1:3000/')
con = sqlite3.connect('TodoDB.db', detect_types=sqlite3.PARSE_DECLTYPES)
con.execute("DROP TABLE user")
con.execute("CREATE TABLE user(id TEXT)")
con.execute("INSERT INTO user(id) values(:id)", {"id": auth.username()})
con.commit()
return "こんにちは、%sさん。このページは閉じてください。 " % auth.username()
@app.route('/reload', methods=['GET', 'POST'])
def reload():
con = sqlite3.connect('TodoDB.db', detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.execute("SELECT * FROM user")
login_user = str(cur.fetchone()[0])
print(login_user)
if login_user == 'a':
cur = con.execute("SELECT * FROM a")
db_col = ['id', 'name', 'completed']
text = cur.fetchall()
data_list = []
for str_data in text:
data = dict(zip(db_col, str_data))
data_list.append(data)
response = {'result': data_list}
print(response)
return make_response(jsonify(response))
elif login_user == 'b':
cur = con.execute("SELECT * FROM b")
db_col = ['id', 'name', 'completed']
text = cur.fetchall()
data_list = []
for str_data in text:
data = dict(zip(db_col, str_data))
data_list.append(data)
response = {'result': data_list}
print(response)
return make_response(jsonify(response))
elif login_user == 'c':
cur = con.execute("SELECT * FROM c")
db_col = ['id', 'name', 'completed']
text = cur.fetchall()
data_list = []
for str_data in text:
data = dict(zip(db_col, str_data))
data_list.append(data)
response = {'result': data_list}
print(response)
return make_response(jsonify(response))
else:
text = []
response = {'result': text}
return make_response(jsonify(response))
@app.route('/post', methods=['GET', 'POST'])
def parse():
data = request.get_json()
con = sqlite3.connect('TodoDB.db', detect_types=sqlite3.PARSE_DECLTYPES)
cur = con.execute("SELECT * FROM user")
login_user = str(cur.fetchone()[0])
print(login_user)
if login_user == 'a':
cur = con.execute("SELECT COUNT(*) FROM sqlite_master WHERE TYPE='table' AND name='a'")
table_a = int(cur.fetchone()[0])
print(table_a)
if (table_a == 1):
con.execute("DROP TABLE a")
con.execute("CREATE TABLE a(id TEXT PRIMARY KEY, name TEXT, completed INTEGER)")
text = data['post_text']
print(text)
for str_data in text:
id_data = str_data['id']
name = str_data['name']
completed = str_data['completed']
if completed == False:
completed_data = 0
else:
completed_data = 1
con.execute("INSERT INTO a(id, name, completed) values(:id, :name, :completed)", {"id": id_data, "name": name, "completed": completed_data})
con.commit()
response = {'result': text}
print(response)
return make_response(jsonify(response))
elif login_user == 'b':
cur = con.execute("SELECT COUNT(*) FROM sqlite_master WHERE TYPE='table' AND name='b'")
table_b = int(cur.fetchone()[0])
print(table_b)
if (table_b == 1):
con.execute("DROP TABLE b")
con.execute("CREATE TABLE b(id TEXT PRIMARY KEY, name TEXT, completed INTEGER)")
text = data['post_text']
print(text)
for str_data in text:
id_data = str_data['id']
name = str_data['name']
completed = str_data['completed']
if completed == False:
completed_data = 0
else:
completed_data = 1
con.execute("INSERT INTO b(id, name, completed) values(:id, :name, :completed)", {"id": id_data, "name": name, "completed": completed_data})
con.commit()
response = {'result': text}
print(response)
return make_response(jsonify(response))
elif login_user == 'c':
cur = con.execute("SELECT COUNT(*) FROM sqlite_master WHERE TYPE='table' AND name='c'")
table_c = int(cur.fetchone()[0])
print(table_c)
if (table_c == 1):
con.execute("DROP TABLE c")
con.execute("CREATE TABLE c(id TEXT PRIMARY KEY, name TEXT, completed INTEGER)")
text = data['post_text']
print(text)
for str_data in text:
id_data = str_data['id']
name = str_data['name']
completed = str_data['completed']
if completed == False:
completed_data = 0
else:
completed_data = 1
con.execute("INSERT INTO c(id, name, completed) values(:id, :name, :completed)", {"id": id_data, "name": name, "completed": completed_data})
con.commit()
response = {'result': text}
print(response)
return make_response(jsonify(response))
else:
text = []
response = {'result': text}
return make_response(jsonify(response))
if __name__=='__main__':
app.run(debug=True, port=5000, threaded=True)
続いて、utils.pyの紹介です。
import MeCab
def wakati(text):
wakatext = text
m = MeCab.Tagger('-0wakati')
return m.parse(wakatext).resplace("¥n","")
最後に、requirements.txtの紹介です。
flask
flask_cors
requests
flask_httpauth
mecab-python3
unidic-lite
React(フロントエンド)の紹介
まずは、ディレクトリ構造の紹介です。
.
├── package-lock.json
├── package.json
├── public
│ └── ...
└── src
├── App.css
├── App.js
├── App.test.js
├── Todo.jsx
├── Todolist.jsx
├── index.css
├── index.js
├── logo.svg
├── reportWebVitals.js
└── setupTests.js
続いて、package.jsonの紹介です。
{
"name": "frontend",
"version": "0.1.0",
"private": true,
"proxy": "http://127.0.0.1:5000",
"dependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.1.3",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
続いて、App.jsの紹介です。
import { useState, useRef } from "react";
import TodoList from "./Todolist";
import { v4 as uuidv4 } from "uuid";
import Axios from "axios";
function App() {
const [todos, setTodos] = useState([]);
const todoNameRef = useRef();
const posting = (text) => {
Axios.post('http://127.0.0.1:5000/post', {
post_text: text
}).then(res => {
console.log(res.data.result);
setTodos(res.data.result);
});
};
const reload = () => {
Axios.get('http://127.0.0.1:5000/reload')
.then(res => {
console.log(res.data.result);
setTodos(res.data.result);
});
};
const handleAddTodo = () => {
const name = todoNameRef.current.value;
if (name === "") return;
const newTodos = [...todos, { id: uuidv4(), name: name, completed: false }];
setTodos(newTodos);
posting(newTodos);
todoNameRef.current.value = null;
};
const toggleTodo = (id) => {
const newTodos = [...todos];
const todo = newTodos.find((todo) => todo.id === id);
todo.completed = !todo.completed;
setTodos(newTodos);
posting(newTodos);
};
const handleClear = () => {
const newTodos = todos.filter((todo) => !todo.completed);
setTodos(newTodos);
posting(newTodos);
};
return (
<div>
<TodoList todos={todos} toggleTodo={toggleTodo} />
<input type="text" ref={todoNameRef} />
<button onClick={handleAddTodo}>タスクを追加</button>
<button onClick={handleClear}>完了したタスクの削除</button>
<button onClick={reload}>更新</button>
<div>残りのタスク:{todos.filter((todo) => !todo.completed).length}</div>
</div>
);
}
export default App;
続いて、Todo.jsxの紹介です。
import React from 'react'
const Todo = ({ todo, toggleTodo }) => {
const handleTodoClick = () => {
toggleTodo(todo.id);
};
return (
<div>
<label>
<input
type="checkbox"
checked={todo.completed}
readOnly
onChange={handleTodoClick}
/>
</label>
{todo.name}
</div>
);
};
export default Todo;
最後に、Todolist.jsxの紹介です。
import React from 'react'
import Todo from './Todo'
const Todolist = ({ todos, toggleTodo }) => {
return todos.map((todo) => <Todo todo={todo} key={todo.id} toggleTodo={toggleTodo} />);
};
export default Todolist
実行画面
localhost:5000にアクセスすると、ユーザー名とパスワードが要求されます。
最後に
このプログラムは、1年以上前に作成したものであったため、仕様を思い出すのに苦労いたしました。このプログラムは、自分でも何とか動かすのに成功していたため、かなり雑だったり冗長だったりすると思います。今回の反省点です。今後、自分が作成したプログラムで、面白そうなものを見つけたら投稿したいと思います。また、間違えているところがあるかもしれませんので、何かあれば、コメントなどで教えていただけると嬉しいです。よろしくお願いいたします。