用python flask框架搭建一个个人博客

Posted _小许_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用python flask框架搭建一个个人博客相关的知识,希望对你有一定的参考价值。

flask作为轻量级框架被广泛应用于中小型的网站开发,不同于javaweb,flask简单易懂适合快速上手,通过以下简单实例制作一个简易网站。

开发前准备
开发技术应用的技术:
前端:jQuery+bootstrap+Ajax
后端:flask+jinjia2+SQLAchemy+pymysql

源码和数据库数据

数据库数据是mysql,数据sql文件在文章最后喔!

工具包通过pip安装即可

pip install sqlalchemy

如果需要安装对应版本的库则用pip install 库名==版本号

pip install sqlalchemy==1.4.36

如果出现pip命令报错的现象,跟换pip的下载源移步pip换源。建议均跟换下载源,国外的下载很慢。

# 切换为清华源
pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

当然也可以不用pip下载,在pycharm中编写代码后通过IDE下载,如下

点击install package pymysql直接安装。

注意这个项目是好久之前的项目了,用的技术比较旧,建议去flask官网学习最新的技术再移植https://www.osgeo.cn/flask/

本次使用的库及版本如下:

项目的目录结构如下:

启动后的页面如下:

登录前只能看文章,登陆后有个人中心,积分,账户,发表文章等功能:


页面有点丑哈,这是老早期的库存了。本来不打算更的,开了要不然就丢了,还是写一篇吧

common目录

# create_connect.py
from sqlalchemy import MetaData
#每个类都要继承flask-SQLAlchemy的db.Mdeol类,封装操作

def extendsModel():
    from main import db
    FactorySession=db.session
    FactoryModel=db.Model
    md=MetaData(bind=db.engine)
    return (FactorySession,FactoryModel,md)
# indentify.py
import random,string


class Figurecode:
    def randomcode(self):
        code=random.sample("abcdefghijklmnopqrstuvwxyz123456789",4)
        return ''.join(code)

# index.py

from os import abort
from flask import Blueprint, render_template, jsonify, session
from common.indentify import Figurecode
from model.tt_article import Article
from model.tt_user import User
from model.tt_favorite import Favorite
import math,re

index=Blueprint("index",__name__)
#自定义函数
def function_cost(username,cost:int):
    user=User()
    user.buy_article_with_credit(username,cost)
    return index.jinja_env.globals.update(buy=function_cost(username,cost))

"""
#实现jinja2前端渲染(Blueprint内置)将其注册到html中就不用再注册了没使用到注册
@index.context_processor
def jinja2_function():
    pass
"""

#定义主页路由
@index.route('/')         #定义路由模板页面
def home():

    article=Article()
    result=article.find_with_nickname(0,5)
    total_page = math.ceil(article.find_view_artilce_count() / 5)
    #print(result1)
    new, max, random=article.get_three_result()
    return render_template('display.html',result=result,total=total_page,page=1,new=new,max=max,random=random)   #将数据传到前端


#定义文章分页路由
@index.route('/page/<int:page>')
def pagelimit(page):
    start=(page-1)*5
    article=Article()
    result=article.find_with_nickname(start,5)
    total_page=math.ceil(article.find_view_artilce_count()/5)
    new, max, random = article.get_three_result()
    return render_template('display.html',result=result,page=page,total=total_page,new=new,max=max,random=random)   #page是当前页参数


@index.route('/type/<int:type>-<int:page>')    #实现类别并分页
def articletypelimit(type,page):
    article=Article()
    start=(page-1)*5
    result1=article.find_article_by_type(type,start,5)
    paing_count=math.ceil(article.get_diffrent_articelcount_by_type(type)/5)
    new, max, random = article.get_three_result()
    return render_template('article.html',result=result1,paing=paing_count,page=page,type=type,new=new,max=max,random=random)

@index.route('/search/<int:page>-<keyword>')
def searchheadline(page,keyword):
    keyword=keyword.strip()
    if len(keyword)==0 or len(keyword)>15 :
        abort(404)
    start=(page-1)*5
    article=Article()
    result1=article.serach_by_headline(keyword,start,5)
    total=math.ceil(article.getcount_by_headline(keyword)/5)
    new, max, random = article.get_three_result()
    return render_template('search.html',result=result1,paing_count=total,keyword=keyword,page=page,new=new,max=max,random=random)

@index.route('/index4')
def function4():
    return render_template('index4.html')

@index.route('/index3')
def function3():
    return render_template('write.html')

@index.route('/article/<int:id>')  #阅读界面
def function2(id):
    article=Article()
    result1=article.get_article_by_id(id)
    if result1 is None:
        abort(404)
    article.update_read_count(id)
    new, max, random = article.get_three_result()
    favorite=Favorite()
    art_collect=favorite.if_collect(id,session.get('userid'))
    return render_template('read.html',result=result1,new=new,max=max,random=random,collect=art_collect)



"""
@index.route('/test')  #实现前端渲染的路由
def function5():
    '''
    article=Article()
    new,max,random=article.get_three_result()
    list=[]
    list.append(dict(new))
    list.append(dict(max))
    list.append(dict(random))
    return  jsonify(list)    #特定的数据结构才能转化为了json
    '''
    
"""

# useregister.py

from flask import Blueprint, request, session, make_response, json, redirect, url_for, render_template
from common.indentify import *
from model.tt_favorite import Favorite
from model.comment import *

from model.tt_user import User

user = Blueprint('user', __name__)


@user.route('/code')
def get_code():
    figurecode = Figurecode()
    code = figurecode.randomcode()
    response = make_response(code)
    session['code'] = code.lower()
    return response


@user.route('/register', methods=['POST'])
def register():
    user = User()
    username = request.form.get('username')
    password = request.form.get('password')
    code = request.form.get('code')
    if user.identify_user_if_same(username) is False:
        return "USERNAME_IS_EXIST"
    elif code == session.get('code'):
        return 'CODE_ERROR'
    else:
        result = user.recive_registic(username, password)
        session['username'] = username
        session['islogin'] = 'true'
        session['userid'] = result.userid
        session['nickname'] = result.nickname
        session['role'] = result.role
        return 'OK'


@user.route('/login', methods=['POST'])
def login():
    user = User()
    username = request.form.get('username').strip()
    password = request.form.get('password').strip()
    code = request.form.get('code')
    print(username, password, code)
    result = user.identify_user_is_true(username, password)
    # if code != session.get('code'):
    #     return 'CODE ERROR'
    print(result,len(result))
    if len(result) == 0:
        return 'USER UNEXIST'
    elif result[0].password != password:
        return 'PASSWORD ERROR'
    else:
        user.add_credit_once(username)
        session['islogin'] = 'true'
        session['userid'] = result[0].userid
        session['username'] = result[0].username
        session['nickname'] = result[0].nickname
        session['role'] = result[0].role
        return 'LOGIN'

@user.route('/logout')
def logout():
    session.clear()     # 清空session,页面跳转,这里重定位
    return redirect('/')#url_for('/')
    #return render_template('display.html')


#定义文章消耗路由
@user.route('/article_cost',methods=['POST'])
def articlecost():
    user=User()
    username = request.form.get('username')
    cost = int(request.form.get('cost'))
    user.buy_article_with_credit(username,cost)
    return 'OK'

#定义文章收藏
@user.route('/article_favorite',methods=['POST'])
def article_favorite():
    favorite=Favorite()
    artid=request.form.get('articleid')
    userid=request.form.get('userid')
    favorite.insert_favorite(artid,userid)
    return 'OK'

#新增评论
@user.route('/add_comments',methods=['POST'])
def add_comments():
    comment=Comment()
    article_id=request.form.get('articleid')
    comments=request.form.get('content')
    if len(comments)<5 or len(comments)>200:
        return 'ERROR'
    else:
        comment.insert_commnet(article_id, comments)
        return 'OK'


# comment.py

from flask import session
from sqlalchemy import Table
from common.create_connect import extendsModel
import time
FactorySession,FactoryModel,md=extendsModel()

class Comment(FactoryModel):
    __table__=Table("comment",md,autoload=True)

    #新增一条评论
    def insert_commnet(self,article_id,comments):
        now = time.strftime('%Y-%m-%d')
        comment=Comment(userid=session.get("userid"),articleid=article_id,content=comments,updatetime=now)
        FactorySession.add(comment)
        FactorySession.commit()


    #根据文章id查询所有评论
    def find_comments_by_articelid(self,articleid):
        result=FactorySession.query(Comment).filter(Comment.articleid==articleid,Comment.hide==0).all()
        return result


"""
if __name__=='__main__':
    comment=Comment()
    print(comment.find_comments_by_articelid(7))
"""

# tt_article.py

from sqlalchemy import Table, func
from model.tt_user import User
from common.create_connect import extendsModel
FactorySession,FactoryModel,md=extendsModel()
class Article(FactoryModel):
    __table__=Table('tt_article',md,autoload=True)

    # 定义操作
    #指定分页limit限制和最新显示offset  select * from user limit param1  offset param2 ;  从(param2+1)条数据开始,取 param1条数据
    def find_with_nickname(self,start,count):

        result=FactorySession.query(Article,User.nickname).join(User,User.userid==Article.userid)\\
            .filter(Article.hidden==0,Article.drafted==1,Article.checked==1)\\
            .order_by(Article.articleid.asc()).limit(count).offset(start).all()
        return result  #将数据库表映射到对象


    #获取文章数量
    def find_view_artilce_count(self):
        count=FactorySession.query(Article).filter(Article.hidden==0,Article.drafted==1,Article.checked==1).count()
        return count

    #根据文章类型获取文章
    def find_article_by_type(self,type,start,count):
        result=FactorySession.query(Article,User.nickname).filter(Article.hidden==0,Article.drafted==1,Article.checked==1,Article.type==type)\\
            .order_by(Article.articleid.asc()).limit(count).offset(start).all()       #按文章类型将所有文章查出来
        return result
    #根据文章类型获取不同文章类型的数量
    def get_diffrent_articelcount_by_type(self,type):
        articel_count = FactorySession.query(Article).filter(Article.hidden == 0, Article.drafted == 1,
                                                     Article.checked == 1,Article.type==type).count()
        return articel_count




    #根据文章标题搜索
    def serach_by_headline(self,headline,start,count):
        result = FactorySession.query(Article, User.nickname).filter(Article.hidden == 0, Article.drafted == 1,
                                                                     Article.checked == 1, Article.headline.like('%'+headline+'%') ) \\
            .order_by(Article.articleid.asc()).limit(count).offset(start).all()  # 按文章类型将所有文章查出来
        return result

    #统计搜索标题的总数量
    def getcount_by_headline(self,headline):
        articel_count = FactorySession.query(Article).filter(Article.hidden == 0, Article.drafted == 1,
                                                             Article.checked == 1, Article.headline.like('%'+headline+'%')).count()
        return articel_count


    #最新文章推荐
    def get_new_article(self):  #倒叙输出文章
        result=FactorySession.query(Article.articleid,Article.headline)\\
            你可以把配置文件升级为包,然后为这些使用场景分别创建不同的配置文件,但是最方便的做法是在单个配置文件中使用python类来组织多个不同类别的配置。

 

下面的代码是personalBlog的配置文件,现在它包含一个基本配置类(BaseConfig),还有其他特定的配置类,即测试配置类(TestingConfig)、开发配置类(DevelopmentConfig)和生产配置类(ProductionConfig),这些特定配置类都继承自基本配置类。

 

personalBlog/setting.py: 使用python类组织配置

 

import os
import sys


basedir = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))

#SQLite URI compatible
WIN = sys.platform.startswith(win)
if WIN:
    prefix = sqlite:///
else:
    prefix = sqlite:////

class BaseConfig(object):
    SECRET_KEY = os.getenv(SECRET_KEY, secret string)

    DEBUG_TB_INTERCREPT_REDIRECTS = False

    SQLALCHEMY_TRACK_MODIFICATIONS = False

    MAIL_SERVER = os.getenv(MAIL_SERVER)
    MAIL_PORT = 465
    MAIL_USE_SSL = True
    MAIL_USERNAME = os.getenv(MAIL_USERNAME)
    MAIL_PASSWORD = os.getenv(MAIL_PASSWORD)
    MAIL_DEFAULT_SENDER = (persnalBlog Admin, MAIL_USERNAME)

    PERSONALBLOG_EMAIL = os.getenv(PERSOANLBLOG_EMAIL)
    PERSONALBLOG_POST_PER_PAGE = 10
    PERSONALBLOG_MAMAGE_POST_PER_PAGE = 15
    PERSONALBLOG_COMMENT_PER_PAGE = 15


class DevelopmentConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = sqlite:/// + os.path.join(basedir, data-dev.db)

class TestingConfig(BaseConfig):
    TESTING = True
    WTF_CSRF_ENABLED = False
    SQLALCHEMY_DATABASE_URI = sqlite:///:memory:  # in-memory database

class ProductionConfig(BaseConfig):
    SQLALCHEMY_DATABASE_URI = os.getenv(DATABASE_URL, sqlite:/// + os.path.join(basedir, data.db))


config = {
    development: DevelopmentConfig,
    testing: TestingConfig,
    production: ProductionConfig
}

 

在新版本的配置中,我们为不同的使用场景设置了不同的数据库URL,避免互相干扰。生产环境下优先从环境变量DATABASE_URL读取,如果没有获取到则使用SQLite,文件名为data.db(在实际生产中我们通常会使用更健壮的DBMS,这里只是示例),在开发时用的数据库文件名为data-dev.db,而测试时的配置则使用SQLite内存型数据库卡。为了获取数据库文件的路径,我们使用os模块的方法创建了一个定位到项目根目录的basedir变量,最终的绝对路径通过os.path模块的方法基于当前脚本的特殊变量__file__获取。

 

在配置文件的底部,我们创建了一个存储配置名称和对应配置类的字典,用于在创建程序实例时通过配置名称来获取对应的配置类。现在我们在创建程序实例后使用app.config.from_object()方法加载配置,传入配置类:

 

from personalBlog.settings import config

app = Flask(personalBlog)
config_name = os.getenv(FLASK_CONFIG, development)
app.config.from_object(config[config_name])

 

我们首先从配置文件中导入匹配配置名到配置类的config字典。为了方便修改配置类型,配置名称会先从环境变量FLASK_CONFIG中导入,从环境变量加载配置可以方便地在不改动代码的情况下切换配置。这个值可以在.flaskenv文件中设置,如果没有获取到,则使用默认值development,对应的配置类即DevelopmentConfig。

 

Flask并不限制你存储和加载配置的方式,可以使用JSON文件存储配置,然后使用app.config.from_json()方法导入;也可以使用INI风格的配置文件,然后自己手动导入。

 

在后续的示例中,我们都使用python类来组织配置。包含敏感信息的配置会从环境变量获取,这些配置值存储在.env文件中。当安装了python-dotenv并使用Flask内置的run命令启动程序时,.env文件的环境变量会被自动设置。

以上是关于用python flask框架搭建一个个人博客的主要内容,如果未能解决你的问题,请参考以下文章

05从头开始用flask搭建个人博客,标签Tags的前端显示和视图

05从头开始用flask搭建个人博客,标签Tags的前端显示和视图

Python Flask 实现 HTML 文件压缩,9 级压缩

Python Flask 实现 HTML 文件压缩,9 级压缩

个人博客搭建Python实现-尝试-遇到的问题(10.1.1)

python web框架Flask后台登录