This commit is contained in:
Akira Tempaku 2025-03-02 15:07:48 +09:00
commit 5309e0593b
Signed by: paku
GPG key ID: 5B4E8402BCC50607
22 changed files with 722 additions and 0 deletions

11
blog/__init__.py Normal file
View file

@ -0,0 +1,11 @@
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
app.config.from_object('blog.config')
db = SQLAlchemy(app)
migrate = Migrate(app, db)
migrate.init_app(app, db)
from blog.views import auth, entries

6
blog/config.py Normal file
View file

@ -0,0 +1,6 @@
SQLALCHEMY_DATABASE_URI = 'sqlite:///blog.db'
SQLALCHEMY_TRACK_MODIFICATIONS = True
DEBUG = True
USERNAME = 'john'
PASSWORD = 'due123'
SECRET_KEY = 'secret key'

17
blog/models/entries.py Normal file
View file

@ -0,0 +1,17 @@
from blog import db
from datetime import datetime
class Entry(db.Model):
__tablename__ = 'entries'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(50), unique=True, nullable=False)
text = db.Column(db.Text, nullable=False)
created_at = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
def __init__(self, title, text):
self.title = title
self.text = text
def __repr__(self):
return f'<Entry id:{self.id} title:{self.title} text:{self.text}>'

View file

@ -0,0 +1,10 @@
{% extends "layout.html" %}
{% block body %}
<form action="{{ url_for('update_entry', id=entry.id) }}" method="post">
<label for="title">タイトル</label>
<input type="text" id="title" name=title value={{ entry.title }}>
<label for="text">本文</label>
<textarea id="text" name=text rows="3">{{ entry.text | safe }}</textarea>
<button type="submit">更新</button>
</form>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "layout.html" %}
{% block body %}
<ul>
{% for entry in entries %}
<li>
<h5>{{ entry.title }}</h5>
<a href="{{ url_for('show_entry', id=entry.id) }}">続きを読む</a>
</li>
{% else %}
投稿がありません
{% endfor %}
</ul>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% extends "layout.html" %}
{% block body %}
<form action="{{ url_for('add_entry') }}" method="post">
<label for="title">タイトル</label>
<input type="text" id="title" name="title">
<label for="text">本文</label>
<textarea id="text" name="text" rows="3"></textarea>
<button type="submit">作成</button>
</form>
{% endblock %}

View file

@ -0,0 +1,14 @@
{% extends "layout.html" %}
{% block body %}
<h2>{{ entry.title }}</h2>
<br>
{{ entry.text }}
<br><br>
投稿日時 {{ entry.created_at }}
<form action="{{ url_for('edit_entry', id=entry.id) }}" method="GET">
<button type="submit">編集</button>
</form>
<form action="{{ url_for('delete_entry', id=entry.id) }}" method="POST">
<button type="submit">削除</button>
</form>
{% endblock %}

View file

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html>
<head>
<title>Flask Blog</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@2/css/pico.min.css">
</head>
<body class="container">
<header>
<nav>
<ul>
<li><a href="/">Flask Blog</a></li>
</ul>
<ul>
{% if not session.logged_in %}
<li><a href="{{ url_for('login')}}">ログイン</a></li>
{% else %}
<li><a href="{{ url_for('new_entry')}}">新規投稿</a></li>
<li><a href="{{ url_for('logout')}}">ログアウト</a></li>
{% endif %}
</ul>
</nav>
</header>
{% for message in get_flashed_messages() %}
<p>{{ message }}</p>
{% endfor %}
<main>
{% block body %}{% endblock %}
</main>
</body>
</html>

10
blog/templates/login.html Normal file
View file

@ -0,0 +1,10 @@
{% extends "layout.html" %}
{% block body %}
<form action="{{ url_for('login')}}" method="post">
<label for="username">ユーザ名</label>
<input type="text" id="username" name="username">
<label for="password">パスワード</label>
<input type="password" id="password" name="password">
<button type="submit">ログイン</button>
</form>
{% endblock %}

31
blog/views/auth.py Normal file
View file

@ -0,0 +1,31 @@
from flask import render_template, request, redirect, session, flash, url_for
from blog import app
from functools import wraps
def login_required(view):
@wraps(view)
def inner(*args, **kwargs):
if not session.get('logged_in'):
return redirect(url_for('login'))
return view(*args, **kwargs)
return inner
@app.route("/login", methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']:
flash("ユーザー名が異なります")
elif request.form['password'] != app.config['PASSWORD']:
flash("パスワードが異なります")
else:
session['logged_in'] = True
flash("ログインしました")
return redirect(url_for("show_entries"))
return render_template("login.html")
@app.route("/logout")
def logout():
session.pop('logged_in', None)
flash("ログアウトしました")
return redirect(url_for("show_entries"))

59
blog/views/entries.py Normal file
View file

@ -0,0 +1,59 @@
from flask import render_template, redirect, url_for, request, flash
from blog import app, db
from blog.models.entries import Entry
from blog.views.auth import login_required
@app.route("/")
@login_required
def show_entries():
entries = Entry.query.order_by(Entry.id.desc()).all()
return render_template('entries/index.html', entries=entries)
@app.route("/entries/new", methods=['GET'])
@login_required
def new_entry():
return render_template('entries/new.html')
@app.route("/entries", methods=["POST"])
@login_required
def add_entry():
entry = Entry(
title=request.form['title'],
text=request.form['text']
)
db.session.add(entry)
db.session.commit()
flash('新しく記事が作成されました')
return redirect(url_for("show_entries"))
@app.route("/entries/<int:id>", methods=["GET"])
@login_required
def show_entry(id):
entry = Entry.query.get(id)
return render_template("entries/show.html", entry=entry)
@app.route('/entries/<int:id>/edit', methods=['GET'])
@login_required
def edit_entry(id):
entry = Entry.query.get(id)
return render_template('entries/edit.html', entry=entry)
@app.route("/entries/<int:id>/update", methods=['POST'])
@login_required
def update_entry(id):
entry = Entry.query.get(id)
entry.title = request.form['title']
entry.text = request.form['text']
db.session.merge(entry)
db.session.commit()
flash('記事が更新されました')
return redirect(url_for('show_entries'))
@app.route("/entries/<int:id>/delete", methods=["POST"])
@login_required
def delete_entry(id):
entry = Entry.query.get(id)
db.session.delete(entry)
db.session.commit()
flash('記事が削除されました')
return redirect(url_for("show_entries"))