MySQL(SQLite3)数据库+Flask框架+HTML搭建个人博客网站

搭建一个博客并实现文章的发送功能,涉及到前后端开发和数据库操作。以下是一个简单的示例,使用Python的Flask框架作为后端,HTML和JavaScript作为前端,SQLite作为数据库。

1. 项目结构

my_blog/
│
├── app.py
├── templates/
│   ├── index.html
│   ├── profile.html
│   └── base.html
├── static/
│   ├── style.css
│   └── script.js
└── blog.db

2. 后端代码 (app.py)

以下数据库使用的是sqlite3(后文介绍MySQL数据库和数组的实现):
from flask import Flask, render_template, request, redirect, url_for, session
import sqlite3
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(16)  # 生成一个 32 字节的随机十六进制字符串

# 初始化数据库
def init_db():
    db = sqlite3.connect('blog.db')
    cursor = db.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT NOT NULL,
            password TEXT NOT NULL
        )
    ''')
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS posts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            title TEXT NOT NULL,
            content TEXT NOT NULL,
            FOREIGN KEY (user_id) REFERENCES users(id)
        )
    ''')
    db.commit()
    db.close()

init_db()

@app.route('/')
def index():
    db = sqlite3.connect('blog.db')
    cursor = db.cursor()
    cursor.execute('SELECT * FROM posts')
    posts = cursor.fetchall()
    db.close()
    return render_template('index.html', posts=posts)

@app.route('/profile', methods=['GET', 'POST'])
def profile():
    if 'username' not in session:
        return redirect(url_for('index'))
    
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        db = sqlite3.connect('blog.db')
        cursor = db.cursor()
        cursor.execute('INSERT INTO posts (user_id, title, content) VALUES (?, ?, ?)', (session['user_id'], title, content))
        db.commit()
        db.close()
        return redirect(url_for('profile'))
    
    db = sqlite3.connect('blog.db')
    cursor = db.cursor()
    cursor.execute('SELECT * FROM posts WHERE user_id = ?', (session['user_id'],))
    posts = cursor.fetchall()
    db.close()
    return render_template('profile.html', posts=posts)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = sqlite3.connect('blog.db')
        cursor = db.cursor()
        cursor.execute('SELECT * FROM users WHERE username = ? AND password = ?', (username, password))
        user = cursor.fetchone()
        db.close()
        if user:
            session['username'] = user[1]
            session['user_id'] = user[0]
            return redirect(url_for('profile'))
        else:
            return "Invalid credentials"
    return render_template('login.html')

@app.route('/logout')
def logout():
    session.pop('username', None)
    session.pop('user_id', None)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)
如果使用的是MySQL数据库,请注意:

使用MySQL作为数据库时,需要对代码进行一些调整。首先,确保你已经安装了所需的Python库:

pip install Flask mysql-connector-python

并将app.py文件的内容用以下代码代替

from flask import Flask, render_template, request, redirect, url_for, session
import mysql.connector
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(16)  # 生成一个 32 字节的随机十六进制字符串

# MySQL database configuration
db_config = {
    'host': 'localhost',
    'user': 'your_username',
    'password': 'your_password',
    'database': 'your_database'
}

# Database initialization
def init_db():
    db = mysql.connector.connect(**db_config)
    cursor = db.cursor()
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            username VARCHAR(255) NOT NULL,
            password VARCHAR(255) NOT NULL
        )
    ''')
    cursor.execute('''
        CREATE TABLE IF NOT EXISTS posts (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            title VARCHAR(255) NOT NULL,
            content TEXT NOT NULL,
            FOREIGN KEY (user_id) REFERENCES users(id)
        )
    ''')
    db.commit()
    db.close()

init_db()

@app.route('/')
def index():
    db = mysql.connector.connect(**db_config)
    cursor = db.cursor()
    cursor.execute('SELECT * FROM posts')
    posts = cursor.fetchall()
    db.close()
    return render_template('index.html', posts=posts)

@app.route('/profile', methods=['GET', 'POST'])
def profile():
    if 'username' not in session:
        return redirect(url_for('index'))
    
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        db = mysql.connector.connect(**db_config)
        cursor = db.cursor()
        cursor.execute('INSERT INTO posts (user_id, title, content) VALUES (%s, %s, %s)', (session['user_id'], title, content))
        db.commit()
        db.close()
        return redirect(url_for('profile'))
    
    db = mysql.connector.connect(**db_config)
    cursor = db.cursor()
    cursor.execute('SELECT * FROM posts WHERE user_id = %s', (session['user_id'],))
    posts = cursor.fetchall()
    db.close()
    return render_template('profile.html', posts=posts)

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = mysql.connector.connect(**db_config)
        cursor = db.cursor()
        cursor.execute('SELECT * FROM users WHERE username = %s AND password = %s', (username, password))
        user = cursor.fetchone()
        db.close()
        if user:
            session['username'] = user[1]
            session['user_id'] = user[0]
            return redirect(url_for('profile'))
        else:
            return "Invalid credentials"
    return render_template('login.html')

@app.route('/logout')
def logout():
    session.pop('username', None)
    session.pop('user_id', None)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)
当然也可以不使用数据库,而用数组储存模拟的数据:

我们用posts数组来存储我们模拟的数据,请将以下代码写入app.py文件

from flask import Flask, render_template, session, redirect, url_for, request
import secrets

app = Flask(__name__)
app.secret_key = secrets.token_hex(16)  # 生成一个 32 字节的随机十六进制字符串

# 模拟数据
posts = [
    (1, 'Author 1', 'First Post', 'This is the content for the first post.'),
    (2, 'Author 2', 'Second Post', 'This is the content for the second post.'),
]

# 主页
@app.route('/')
def index():
    return render_template('index.html', posts=posts)

# 登录视图(示例用简单的登录方式)
@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form.get('username')
        return redirect(url_for('index'))
    return render_template('login.html')

# 个人中心
@app.route('/profile', methods=['GET', 'POST'])
def profile():
    if 'username' not in session:
        return redirect(url_for('login'))
    
    if request.method == 'POST':
        title = request.form.get('title')
        content = request.form.get('content')
        # 在此处可以添加代码将新帖子保存到数据库或列表中
        posts.append((len(posts) + 1, session['username'], title, content))

    return render_template('profile.html', posts=posts)

# 登出视图
@app.route('/logout')
def logout():
    session.pop('username', None)
    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)
  • 设置密钥secret_key 是用于加密 Flask 应用中的某些数据的一个字符串,特别是与用户会话相关的数据。Flask 使用这个密钥来进行数据签名(比如处理 Flask 的 session 对象时)。
  • 用户会话保护:该密钥有助于防止伪造用户会话。使用时,Flask 会对存储在会话中的数据进行签名,以防止攻击者修改会话内容。

你并不需要知道这个密钥的具体值 ,但你有方法可以获取这个随机生成的密钥,感兴趣自己多了解了解。

3. 前端代码

基础模板 (templates/base.html)
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Blog{% endblock %}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <nav>
        <a href="{{ url_for('index') }}">Home</a>
        {% if 'username' in session %}
        <a href="{{ url_for('profile') }}">Profile</a>
        <a href="{{ url_for('logout') }}">Logout</a>
        {% else %}
        <a href="{{ url_for('login') }}">Login</a>
        {% endif %}
    </nav>
    <div class="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>
首页 (templates/index.html)
{% extends "base.html" %}

{% block title %}Home{% endblock %}

{% block content %}
<h1>Welcome to My Blog</h1>
<ul>
    {% for post in posts %}
    <li>
        <h2>{{ post[2] }}</h2>
        <p>{{ post[3] }}</p>
    </li>
    {% endfor %}
</ul>
{% endblock %}

这段代码是一个使用 Jinja2 模板引擎的 HTML 模板文件。它继承了一个名为 base.html 的基础模板,并在其中定义了两个块(titlecontent)。以下是代码的详细解释:

1. {% extends "base.html" %}

  • 作用 : 这行代码表示当前模板继承自 base.html 文件。base.html 通常是一个包含网站通用结构的基础模板,比如头部、导航栏、页脚等。
  • 继承 : 通过继承,当前模板可以使用 base.html 中定义的结构,并在特定位置插入自己的内容。

2. {% block title %}Home{% endblock %}

  • 作用 : 这行代码定义了一个名为 title 的块,并将其内容设置为 Home
  • 用途 : 通常,title 块用于定义页面的标题,这个标题会显示在浏览器的标签栏中。

3. {% block content %}{% endblock %}

  • 作用 : 这行代码定义了一个名为 content 的块,并在其中插入了页面的主要内容。
  • 用途 : content 块通常用于放置页面的主要内容,比如文章列表、表单等。

4. <h1>Welcome to My Blog</h1>

  • 作用: 这是一个一级标题,显示在页面的主要内容区域。

5. <ul>{% for post in posts %}

  • 作用 : 这是一个无序列表,用于显示文章列表。{% for post in posts %} 是一个循环语句,遍历 posts 列表中的每一项。
  • 用途 : 在循环中,每一项 post 都会生成一个列表项 <li>,并显示文章的标题和内容。

6. {``{ post[2] }}{``{ post[3] }}

  • 作用 : 这些是变量插值,用于显示 post 对象的特定属性。
    • post[2] 通常表示文章的标题。
    • post[3] 通常表示文章的内容。
个人中心 (templates/profile.html)
{% extends "base.html" %}

{% block title %}Profile{% endblock %}

{% block content %}
<h1>Welcome, {{ session['username'] }}!</h1>
<form action="{{ url_for('profile') }}" method="post">
    <label for="title">Title:</label>
    <input type="text" id="title" name="title" required>
    <label for="content">Content:</label>
    <textarea id="content" name="content" required></textarea>
    <button type="submit">Post</button>
</form>
<ul>
    {% for post in posts %}
    <li>
        <h2>{{ post[2] }}</h2>
        <p>{{ post[3] }}</p>
    </li>
    {% endfor %}
</ul>
{% endblock %}

4. 静态文件 (static/style.css)

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 0;
    background-color: #f4f4f4;
}

nav {
    background-color: #333;
    overflow: hidden;
}

nav a {
    float: left;
    display: block;
    color: #f2f2f2;
    text-align: center;
    padding: 14px 16px;
    text-decoration: none;
}

nav a:hover {
    background-color: #ddd;
    color: black;
}

.content {
    padding: 20px;
    margin: 20px;
}

form {
    margin-bottom: 20px;
}

label {
    display: block;
    margin-bottom: 5px;
}

input, textarea {
    width: 100%;
    padding: 8px;
    margin-bottom: 10px;
    box-sizing: border-box;
}

button {
    padding: 10px 20px;
    background-color: #333;
    color: white;
    border: none;
    cursor: pointer;
}

button:hover {
    background-color: #555;
}

我暂时还没有编写login和logout的html文件,如果感兴趣可以自己先编写。不过在首页你可以直接点击profile进入个人中心,这并不影响。至于如何运行flask项目,可以在终端使用以下代码。

flask --app app run --host=0.0.0.0

5. 运行应用

  1. 确保你已经安装了Flask。如果没有,可以通过pip install flask安装。
  2. 运行app.py,访问http://127.0.0.1:5000即可看到博客首页。
  3. 登录后可以进入个人中心,发表文章。
相关推荐
月光水岸New2 小时前
Ubuntu 中建的mysql数据库使用Navicat for MySQL连接不上
数据库·mysql·ubuntu
狄加山6752 小时前
数据库基础1
数据库
我爱松子鱼2 小时前
mysql之规则优化器RBO
数据库·mysql
chengooooooo2 小时前
苍穹外卖day8 地址上传 用户下单 订单支付
java·服务器·数据库
Rverdoser3 小时前
【SQL】多表查询案例
数据库·sql
Galeoto3 小时前
how to export a table in sqlite, and import into another
数据库·sqlite
人间打气筒(Ada)4 小时前
MySQL主从架构
服务器·数据库·mysql
leegong231114 小时前
学习PostgreSQL专家认证
数据库·学习·postgresql
喝醉酒的小白4 小时前
PostgreSQL:更新字段慢
数据库·postgresql
敲敲敲-敲代码4 小时前
【SQL实验】触发器
数据库·笔记·sql