> 文章列表 > Python进阶项目--只因博客(bootstrap+flask+mysql)

Python进阶项目--只因博客(bootstrap+flask+mysql)

Python进阶项目--只因博客(bootstrap+flask+mysql)

前言

	1.全民制作人们大家好,我是练习时长两年半的个人练习生只因坤坤,喜欢唱,跳,rap,篮球,music......在今后的节目中,我还准备了很多我自己作词、作曲、编舞的原创作品,期待的话,请多多为我投票吧😎2. 只因博客:不是ikun请勿进,谢谢~~3. Python进阶项目,总体框架为"bootstrap+flask+mysql",适合Flask框架入门学习使用💖,本博客未使用restful技术,可以实现一些博客的基本功能,咋从配置虚拟环境开始~~5. 最后,关注我,一定能学会Flask框架,学不学无所谓hhh🎉🎉🎉

只因首页效果图

在这里插入图片描述

1. 配置虚拟环境

1.1 具体步骤

# 打开cmd
1. 安装virtualenv (windows操作系统)pip install virtualenv virtualenvwrapper-win2. workon查看虚拟环境workon3. mkvirtualenv创建新的虚拟环境mkvirtualenv flask2env4. rmvirtualenv删除虚拟环境rmvirtualenv flask2env5. 进入虚拟环境workon flask1env6. 在虚拟环境中安装flask2pip install flask==2.2.37. 打开Pycharm专业版,创建Flask个人博客并配置好虚拟环境flask1env

1.2 Pycharm虚拟环境的配置

在这里插入图片描述

2. 安装插件

2.1 Flask的ORM

Flask使用Python自带的ORM: SQLAlchemy
针对于Flask的支持,安装插件: flask-sqlalchemy
命令: pip install flask-sqlalchemy

2.2 数据迁移

命令: pip install flask-migrate

2.3 插件代码

from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migratedb = SQLAlchemy()
migrate = Migrate()def init_exts(app):db.init_app(app=app)migrate.init_app(app=app, db=db)

3.flask博客基本框架

3.1 app.py

from App import create_appapp = create_app()if __name__ == '__main__':app.run(debug=True)

3.2 init.py

from flask import Flask
from .views.views import blog
from .views.views_admin import admin
from .exts import init_exts
# __init__.py :初始化文件,创建Flask应用def create_app():app = Flask(__name__)# 1. 注册蓝图app.register_blueprint(blueprint=blog) # 博客前端页面app.register_blueprint(blueprint=admin) # 博客后台管理# 2. 配置数据库# db_uri = 'sqlite:///sqlite3.db'  # sqlite配置db_uri = 'mysql+pymysql://root:cocair@localhost:3306/blogdb'  # mysql的配置app.config['SQLALCHEMY_DATABASE_URI'] = db_uriapp.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False  # 禁止对象追踪修改# 3. 初始化插件init_exts(app=app)return app

3.3 views.py

from flask import Blueprint
from ..models.models import * # ..引用堂兄弟文件# 蓝图
blog = Blueprint('blog', __name__)@blog.route('/')
def index():return 'index'

3.4 views_admin.py

from flask import Blueprint
from ..models.models_admin import *# 蓝图
admin = Blueprint('admin', __name__)@admin.route('/')
def index():return 'index'

3.5 models.py

from ..exts import dbclass User(db.Model):__tablename__ = 'tb_user'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(30), unique=True, index=True)age = db.Column(db.Integer, default=1)

3.6 models_admin.py

from ..exts import dbclass User(db.Model):__tablename__ = 'tb_user'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(30), unique=True, index=True)age = db.Column(db.Integer, default=1)
此时初始的Flask框架就搭建好啦~~
不能理解的小伙伴可以看看这几篇博客🍩🍩🍩

Flask入门和视图–01
Flask会话技术和Flask模板语言–02
Flask模型基础–03

4.设计数据库

敲黑板!!🎮看此片段时建议先看看这篇博客图🎫
Flask数据迁移详细步骤

初始的框架搭建好后,就需要开始数据库的设计👌~~1.models.py(用户端数据库)分类数据库:CategoryModel文章数据库:ArticleModel相册数据库:PhotoModel其中分类数据库与文章数据库是一对多关系2.models_admin.py(管理员端数据库)管理员数据库:AdminUserModel

4.1 models.py

from ..exts import db# 分类 : 文章 ==> 1 : N
# 分类
class CategoryModel(db.Model):__tablename__ = 'tb_category'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(30), unique=True)describe = db.Column(db.Text(), default="describe")# 2.设置联系&懒加载articles = db.relationship("ArticleModel", backref="category", lazy="dynamic")# 文章
class ArticleModel(db.Model):__tablename__ = 'tb_article'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(30), unique=True)key = db.Column(db.String(255), default="keyword")content = db.Column(db.Text(), default="content")img = db.Column(db.Text(), default="img")# 1.(N): 设置外键category_idcategory_id = db.Column(db.Integer, db.Foreignkey(CategoryModel.id))# 相册
class PhotoModel(db.Model):__tablename__ = 'tb_photo'id = db.Column(db.Integer, primary_key=True, autoincrement=True)url = db.Column(db.Text(30)) # 由于考虑到文件路径过长,所以使用Text()name = db.Column(db.String(255), unique=True)describe = db.Column(db.Text(), default="describe")

4.2 models_admin.py

from ..exts import dbclass AdminUserModel(db.Model):__tablename__ = 'tb_admin user'id = db.Column(db.Integer, primary_key=True, autoincrement=True)name = db.Column(db.String(30), unique=True, index=True)passwd = db.Column(db.String(30))

4.3 数据迁移

1.数据迁移命令:1. 在cmd或Terminal先进入项目目录:2. 然后输入命令:flask db init  创建迁移文件夹migrates, 只调用一次flask db migrate  生成迁移文件flask db upgrade  执行迁移文件中的升级flask db downgrade  执行迁移文件中的降级2.如果执行flask db init报错😢:
ValueError: the greenlet library is required to use this function.
则需要执行:pip install greenlet即可

4.4 Mysql Workben

在这里插入图片描述

4.41 添加测试数据

tb_admin user

在这里插入图片描述

tb_category

在这里插入图片描述

tb_article

在这里插入图片描述

tb_photo

在这里插入图片描述

1. 测试数据量少时,建议直接在数据库里添加...
2. images/photos是原先就加载在模板(templates)文件下的模板,所以此处路径为/static/home/photos/***.jpeg
3. 前端static、templates模块,在文章末我会给出链接...

5. 编码实现–功能端

只展示核心代码~~
源码我会放在文章末的链接哦爱心🌹🌹🌹

5.1 views

5.11 blog_index

# 博客_首页
@blog.route('/')
@blog.route('/index/')
def blog_index():photos = PhotoModel.query.limit(6)categorys = CategoryModel.query.all()articles = ArticleModel.query.all()commends = articles[0:4] # 用切片取到前4张return render_template('home/index.html', photos=photos, categorys=categorys, articles=articles, commends=commends)

5.12 blog_photos

@blog.route('/photos/')
def blog_photos():photos = PhotoModel.query.all()return render_template('home/photos.html', photos=photos)

5.13 blog_route

# 博客_我的日记
@blog.route('/article/')
def blog_article():articles = ArticleModel.query.all()return render_template('home/article.html', articles=articles)

5.14 blog_about

# 博客_关于我
@blog.route('/about/')
def blog_about():photos = PhotoModel.query.limit(6)categorys = CategoryModel.query.all()return render_template('home/about.html', photos=photos, categorys=categorys)

5.2 templates

5.21 index.html

<main class="r_box"><ul>{% for article in articles %}<li><i><a href="#"><img src="{{ article.img }}"></a></i><h3><a href="#">{{ article.name }}</a></h3><p> {{ article.content }} </p></li>{% endfor %}</ul></main>

5.22 article.html

<main class="r_box"><ul>{% for article in articles %}<li><i><a href="#"><img src="{{ article.img }}"></a></i><h3><a href="#"> {{ article.name }} </a></h3><p> {{ article.content }} <p></li>{% endfor %}</ul></main>

5.3 只因博客效果图🎉🎉🎉

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.编码实现–后台管理系统

此后只展示后端代码啦~~
前端只展示核心代码(太长了...)

6.1 登录 & 注销

6.11 登录装饰器

# 装饰器:登录验证
def login_required(fn):@wraps(fn)def inner(*arg, **kwargs):# 判断是否登录# 获取cookie,得到登录的用户user_id = request.cookies.get("user_id",None)if user_id:# 登录过进入后台管理系统user = AdminUserModel.query.get(user_id)request.user =userreturn fn(*arg, **kwargs)else:# 如果没有登录,则跳转登录页面return redirect('/admin/login')return inner

不理解装饰器,可以参考此篇博客💣💣
好的…本来想附上链接的…发现忘记写了🙄😅
不过不要急,后期,一定补上(考研党,谅解一下…)
简单说下,装饰器就是一个代码复用模板,这里因为登录验证代码每个功能实现中都需用到,所以直接封装复用,格式为在需要用到此模块的视图函数上加@login_required

6.12 views_admin.py

# 后台管理--首页
@admin.route('/admin/')
@admin.route('/admin/index/')
@login_required
def admin_index():user = request.usercategorys = CategoryModel.query.filter()articles = AdminUserModel.query.filter()photos = PhotoModel.query.filter()# 登录成功,跳转首页return render_template("admin/index.html", username=user.name, categorys=categorys, articles=articles, photos=photos)
"""未加登录装饰器user_id = request.cookies.get("user_id",None)if user_id:user = AdminUserModel.query.get(user_id)categorys = CategoryModel.query.filter()articles = AdminUserModel.query.filter()photos = PhotoModel.query.filter()# 登录成功,跳转首页return render_template("admin/index.html", username=user.name, categorys=categorys, articles=articles, photos=photos)else:# 返回登录页面return redirect("/admin/login")
"""# 后台管理--登录
@admin.route('/admin/login/', methods=["GET","POST"])
def admin_login():if request.method == "GET":return render_template("admin/login.html")elif request.method == "POST":username = request.form.get("username") #request.form.get()userpwd = request.form.get("userpwd")user = AdminUserModel.query.filter_by(name=username, passwd=userpwd).first()if user:# 登录成功response = redirect("/admin/index/")# set_cookie(参数新名字\\参数\\cookie保存时间)response.set_cookie("user_id", str(user.id), max_age=1 * 24 * 3600)return responseelse:return "Login false"# 后台管理--注销
@admin.route('/admin/logout/')
def admin_logout():response = redirect("/admin/login/")response.delete_cookie("user_id")return response

6.12 index.html

{# 如果用户登录了就显示用户名,如果没有登录就显示登录按钮 #}
{% if username %}<li><a href="#"> 欢迎您: {{ username }} </a></li><li><a href="/admin/logout/"> 注销 </a></li>
{% else %}<li><a href="/admin/login/"> 登录 </a></li>
{% endif %}
...

6.13 后台管理 登录页面&首页

在这里插入图片描述
在这里插入图片描述

6.2 分类管理

6.21 后台管理–分类

# 后台管理--分类
@admin.route('/admin/category/')
@login_required # 登录装饰器
def admin_category():user = request.usercategorys = CategoryModel.query.all()return render_template("admin/category.html", username=user.name, categorys=categorys)

在这里插入图片描述

6.22 后台管理–添加分类

# 后台管理--添加分类
@admin.route('/admin/addcategory/', methods=['GET','POST'])
@login_required
def admin_addcategory():if request.method == "POST":# 添加分类name = request.form.get("name")describe = request.form.get("describe")category = CategoryModel()category.name = namecategory.describe = describetry:db.session.add(category)db.session.commit()except Exception as e:print("e:",e)db.session.rollback()return redirect("/admin/category/")else:return '请求方式错误!'

在这里插入图片描述

6.23 后台管理–删除分类

views_admin.py
# 后台管理--删除分类
@admin.route('/admin/del_category/', methods=['GET','POST'])
@login_required
def admin_del_category():if request.method == "POST":# 删除分类id = request.form.get("id")category = CategoryModel.query.get(id)try:db.session.delete(category)db.session.commit()except Exception as e:print("e:",e)return jsonify({'code' : 200, 'msg' : '删除成功!'})else:return jsonify({'code' : 400, 'msg' : '请求方式错误!'})
**category.html (javascript)
<script>//是否确认删除$(function () {$("#main table tbody tr td a").click(function () {var that = $(this);var id = that.attr("cid"); //对应idif (event.srcElement.outerText === "删除") {if (window.confirm("此操作不可逆,是否确认?")) {// Ajax请求: 前后端分离// 点击删除按钮:$.post('/admin/del_category/', {'id': id}, function(data){console.log(data.msg)if (data.code == 200) {location.reload()}})}}})});
</script>

6.24 修改分类

后台管理–修改分类
# 后台管理--修改分类
@admin.route('/admin/updatecategory/<id>/', methods=['GET','POST'])
@login_required
def admin_update_category(id):if request.method == "GET":category = CategoryModel.query.get(id)return render_template("admin/category_update.html", category=category, username=request.user.name)elif request.method == "POST":name = request.form.get("name")describe = request.form.get("describe")category = CategoryModel.query.get(id)category.name = namecategory.describe = describetry:db.session.commit()except Exception as e:print("e",e)db.session.rollback()return redirect("/admin/category/")else:return "请求方式错误"
修改分类的页面

在这里插入图片描述

修改分类的逻辑
修改分类的逻辑(这里比较绕,解释一下~~)✨✨✨
1. category.html页面中点击修改,此时会通过jinjia2模板中的{{ category.id }}(前端代码里有,自行下载一下~~)传输给给后端数据(id),回到views_admin.py中
2. 当request.method收到get的请求,便会执行category = CategoryModel.query.get(id),通过此代码会查找数据库中为id的对象给category赋值,然后传入category_update.html页面
3. 通过前端category_update.html页面,修改新值后,此时再通过前端提交,回到views_admin.py中
4. 执行elif request.method == "POST":后的代码

6.3 文章管理

6.31 文章管理

# 后台管理--分类
@admin.route('/admin/category/')
@login_required
def admin_category():user = request.usercategorys = CategoryModel.query.all()return render_template("admin/category.html", username=user.name, categorys=categorys)

6.32 删除文章

逻辑解释
1. 删除文章并不需要新创建h5
2. 只需用到javascript技术即可🐾
删除文章
# 后台管理--删除分类
@admin.route('/admin/del_category/', methods=['GET','POST'])
@login_required
def admin_del_category():if request.method == "POST":# 删除分类id = request.form.get("id")category = CategoryModel.query.get(id)try:db.session.delete(category)db.session.commit()except Exception as e:print("e:",e)return jsonify({'code' : 200, 'msg' : '删除成功!'})else:return jsonify({'code' : 400, 'msg' : '请求方式错误!'})
javascript
<script>//是否确认删除$(function () {$("#main table tbody tr td a").click(function () {var that = $(this);var id = that.attr("aid");  //对应idif (event.srcElement.outerText == "删除") {if (window.confirm("此操作不可逆,是否确认?")) {// 删除文章$.post('/admin/delarticle/', {'id': id}, function (data){console.log(data.msg)if (data.code == 200) {location.reload()}else {alert(data.msg)}})}}});});
</script>
展示

在这里插入图片描述

6.33 添加文章

# 后台管理--添加文章
@admin.route('/admin/addarticle/', methods=["GET","POST"])
@login_required
def admin_add_article():if request.method == "GET":categorys = CategoryModel.query.all()return render_template("admin/article_add.html", username=request.user.name, categorys=categorys)elif request.method == "POST":name = request.form.get("name")keywords = request.form.get("keywords")content = request.form.get("content")category = CategoryModel.query.get("category")img = request.files.get("img")# 图片存储路径img_name = f'{time.time()}-{img.filename}' # 加时间戳,因为图片可能会重复img_url = f'/static/home/uploads/{img_name}'# 添加文章try:article = ArticleModel()article.name = namearticle.key = keywordsarticle.content = contentarticle.category_id = categoryarticle.img = img_urldb.session.add(article)db.session.commit()except Exception as e:db.session.rollback()db.session.flush()print("e:",e)else:# 如果上面添加到数据库成功,那么手动将图片存入本地img_data = img.read()with open(f'App/{img_url}', 'wb') as fp:fp.write(img_data)fp.flush()return redirect('/admin/article/')
对图片进行添加时注意两个操作1.保存文件的路径时建议加上时间戳,因为加上时间戳可以防止同名相册的存在2.除此之外,还需要写入文件的操作

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cePSKIJ4-1681975888466)(C:\\Users\\57218\\Desktop\\添加文章.png)]

6.34 更新文章

# 后台管理-修改文章
@admin.route('/admin/updatearticle/<id>/', methods=['GET', 'POST'])
@login_required
def admin_update_article(id):article = ArticleModel.query.get(id)if request.method == 'GET':categorys = CategoryModel.query.all()return render_template('admin/article_update.html',username=request.user.name,categorys=categorys,article=article)elif request.method == 'POST':# 修改文章name = request.form.get('name')keywords = request.form.get('keywords')content = request.form.get('content')category = request.form.get('category')img = request.files.get('img')# 图片存储路径img_name = f'{time.time()}-{img.filename}'img_url = f'/static/home/uploads/{img_name}'# 修改文章try:article.name = namearticle.keyword = keywordsarticle.content = contentarticle.img = img_url  # 图片路径article.category_id = categorydb.session.commit()except Exception as e:db.session.rollback()db.session.flush()print('e:', e)else:# 如果上面添加到数据库成功,那么手动将图片存入本地img_data = img.read()with open(f'App/{img_url}', 'wb') as fp:fp.write(img_data)fp.flush()return redirect('/admin/article/')

7. 源码链接

所以说,为什么关注又取关了呢?💔
难道不是ikun了吗?!😢😢
hhh,开个小玩笑😉,切记玩梗归玩梗,学习才是为主!!
关注我,一定能会Flask框架,学不会无所谓~~
本源码还有瑕疵,恳请大家在评论区点评(接受一切质疑,批评)🚀🚀
源码链接在这里~~

源码+哥哥典藏图+sql文件