Flask + flask_sqlalchemy + jq 完成书籍展示、新增、删除功能

后端代码

from flask import Flask, render_template, request, jsonify
from flask_wtf.csrf import CSRFProtect
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)

# 使用防csrf保护APP
csrf = CSRFProtect(app)


class Config(object):
    # sqlalchemy的配置参数
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:123456@192.168.3.109:3306/test"

    # 设置sqlalchemy自动更跟踪数据库
    SQLALCHEMY_TRACK_MODIFICATIONS = True

    SECRET_KEY = "doiso7fd89fyd9^(fsd"


app.config.from_object(Config)
db = SQLAlchemy(app)


# 定义作者模型类
class Author(db.Model):
    __tablename__ = "tbl_author"

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))


# 定义书籍模型类
class Book(db.Model):
    __tablename__ = "tbl_book"

    id = db.Column(db.Integer, primary_key=True)
    book_name = db.Column(db.String(50))
    author_id = db.Column(db.Integer)


@app.route("/")
def index():
    book_num = db.session.query(Book).all()
    page_num = len(book_num)//10
    if len(book_num) % 10 > 0:
        page_num += 1
    page_list = range(1, page_num + 1)
    page = request.args.get("page")
    if not page:
        page = 1
    else:
        page = int(page)

    # 查询数据
    ret_list = db.session.query(Book.id, Book.book_name, Author.name)\
        .outerjoin(Author, Book.author_id == Author.id).order_by(Book.id)\
        .slice((page-1)*10, page*10)\
        .all()
    return render_template("book.html", books=ret_list, page_list=page_list)


@csrf.exempt      # 取消csrf保护
@app.route("/del", methods=["POST"])
def delete():
    """删除书籍"""
    # 提取参数
    # 如果前端发送的请求体数据是json格式,get_json会解析成字典
    # get_json 要求前端传送的数据的Content-Type: application/json
    req_dict = request.get_json()
    book_id = int(req_dict.get("book_id"))
    if book_id == '':
        resp_data = {"code": 1, "msg": "传入的id为空", "data": {}}
        return jsonify(resp_data)

    books_id = db.session.query(Book.id).all()

    if (book_id,) not in books_id:
        resp_data = {"code": 1, "msg": "删除的书籍不存在", "data": {}}
        return jsonify(resp_data)

    # 删除数据
    book = db.session.query(Book).get(book_id)
    db.session.delete(book)
    db.session.commit()

    # 构造响应数据
    resp_data = {"code": 0, "msg": "删除成功", "data": {}}
    return jsonify(resp_data)


@app.route("/add", methods=["POST"])
def add():
    """增加书籍"""
    # 获取请求信息
    req_dict = request.get_json()
    book_name = req_dict.get("name")
    author_name = req_dict.get("author")
    # 检查请求信息
    if book_name == "" or author_name == "":
        # 为空则提示
        resp_data = {"code": 1, "msg": "输入的数据不能为!", "data": {}}
        return jsonify(resp_data)

    # 检查作者名称是否存在
    authors_name = db.session.query(Author.name).all()
    new_author = None
    new_id = None
    if (author_name,) not in authors_name:
        # 不存在就创建
        new_id = db.session.query(Author.id).order_by(-Author.id).first()[0] + 1
        new_author = Author(id=new_id, name=author_name)
        # 将新增数据加入会话中,等待最后一起提交
        db.session.add(new_author)

    # 获取作者的id
    if new_author:
        author_id = new_id
    else:
        author_id = db.session.query(Author.id).filter(Author.name == author_name).one()[0]

    # 检查请求的书名
    books_name = db.session.query(Book.book_name).filter(Book.author_id == author_id).all()
    if (book_name,) in books_name:
        # 存在则提示
        resp_data = {"code": 1, "msg": "书籍已存在", "data": {}}
        return jsonify(resp_data)

    new_book = Book(book_name=book_name, author_id=author_id)
    db.session.add(new_book)

    # 留个长度超长BUG,验证该异常
    try:
        db.session.commit()
        resp_data = {"code": 0, "msg": "创建成功", "data": {}}
        return resp_data
    except Exception as e:
        db.session.rollback()
        resp_data = {"code": 1, "msg": F"提交失败:{e}", "data": {}}
        return resp_data


if __name__ == '__main__':
    # 删除所有表格(慎用)
    # db.drop_all()
    # db.create_all()

    app.run()

模板代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <!--  如果开启csrf保护,需要定义如下代码  -->
   <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>book_info</title>
    <script src="/static/js/jquery-1.12.4.min.js"></script>
    <script>
        $(function(){
            var book_name = $("#book_name")
            var book_author = $("#book_author")
            var csrftoken = $('meta[name=csrf-token]').attr('content')
            $.ajaxSetup({
                beforeSend: function(xhr, settings) {
                    if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type)) {
                        xhr.setRequestHeader("X-CSRFToken", csrftoken)
                    }
                }
            });

            // 获取URL后参数的值,name为参数名
            $.getUrlParam = function (name) {
                var reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
                var r = window.location.search.substr(1).match(reg);
                if (r != null) return unescape(r[2]); return null;
            }
            var current_page = $.getUrlParam("page")
            
            // 定义ajax请求函数
            var req_post = function(url, data){
                // 将js中的对象转换为 json字符串
                var req_json = JSON.stringify(data);
                $.ajax({
                    url: url,
                    type: "post",
                    // 申明传给后端的字符类型
                    contentType: "application/json",
                    dataType: 'json',
                    data: req_json
                }).done(function(resp){
                    if (resp.code == 0)
                    {
                        if (url == "/del" && $(".del").length == 1){
                            current_page = current_page - 1
                            location.href = "/?page=" + current_page
                        }else{
                            location.href = "/?page=" + current_page
                        }
                    }
                    alert(resp.msg)
                }).fail(function(){
                    console.log("请求失败")
                });
            };

            // 创建书籍
            $("#book_create").click(function(){
                b_name = book_name.val();
                b_author = book_author.val();
                var data = {'name': b_name, 'author': b_author};
                req_post("/add", data)
            });

            // 删除书籍
            $(".del").click(function(){
                var id = $(this).siblings()[0].innerHTML;
                var data = {'book_id': id}
                console.log("删除id为:" + id)
                req_post("/del",data)
            });

            // 分页
            $(".page div").click(function(){
                var page_num = $(this)[0].innerHTML;
                var data = "page=" + page_num
                location.href = "/?" + data
            });
        })
    </script>
    <style>
        * {
            font-size: 16px;
        }
        #add {
            margin-top: 50px;
            margin-left: 100px;
            float: left;
        }

        #add div {
            width: 400px;
            margin: 10px auto 0px;
        }

        #book_name, #book_author {
            width: 200px;
            border: 1px solid #000;
        }
        
        #book_create {
            width: 100px;
            margin: auto;
        }

        #book {
            width: 500px;
            background-color: rgba(113, 255, 161, 0.952);
            margin-left: 50px;
            float: left;
        };

        .th, .tr {
            width: 500px;
            height: 20px;
        }

        .th div, .tr div{
            width: 200px;
            height: 30px;
            float: left;
            text-align: center;
            line-height: 30px;
        }

        .clearfix:before,.clearfix:after{
            content:"";
            display:table;
        }
        .clearfix:after{
            clear:both;
        }
        .clearfix{
            zoom:1;
        }

        div.del{
            width: 100px;
            color: rgb(49, 0, 185);
        }

        div.th3 {
            width: 100px;
        }

        .del:hover, .page div:hover{
            cursor: pointer;
        }

        .page {
            width: 280px;
            height: 20px;
            margin: 20px;
        }

        .page div {
            width: 20px;
            height: 20px;
            float: left;
            margin: 0px 5px;
            text-align: center;
            line-height: 20px;
        }

    </style>
</head>
<body>
<div id="app">
    <div id="add">
        <div>书籍名称:<input type="text" id="book_name"></div> 
        <div>书籍作者:<input type="text" id="book_author"></div>
        <div><input type="submit" id="book_create" value="新增"></div>
    </div>
    <div id="book">
        <div class="th clearfix">
            <div>书籍名称</div>
            <div>作者</div>
            <div class="th3">操作</div>
        </div>
        {% for book in books %}
        <div class="tr clearfix">
            <div style="display: none;">{{ book[0] }}</div>
            <div>{{ book[1] }}</div>
            <div>{{ book[2] }}</div>
            <div  class="del" >删除</div>
        </div>
        {% endfor %}
        <div class="page">
            {% for page in page_list %}
                <div>{{ page }}</div>

            {% endfor %}
        </div>
    </div>
</div>
</body>
</html>

界面效果

 

 

原文地址:https://www.cnblogs.com/testlearn/p/14136218.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Jinja2:是Python的Web项目中被广泛应用的模板引擎,是由Python实现的模板语言,Jinja2 的作者也是 Flask 的作者。他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,其是Flask内置的模板语言。
Fullcalendar日历使用,包括视图选择、事件插入、编辑事件、事件状态更改、事件添加和删除、事件拖动调整,自定义头部,加入el-popover显示图片、图片预览、添加附件链接等,支持手机显示。
监听QQ消息并不需要我们写代码,因为市面上已经有很多开源QQ机器人框架,在这里我们使用go-cqhttp官方文档:go-cqhttp如果您感兴趣的话,可以阅读一下官方文档,如果不想看,直接看我的文章即可。
【Flask框架】—— 视图和URL总结
python+web+flask轻量级框架的实战小项目。登录功能,后续功能可自行丰富。
有了这个就可以配置可信IP,关键是不需要企业认证,个人信息就可以做。
本专栏是对Flask官方文档中个人博客搭建进行的归纳总结,与官方文档结合事半功倍。 本人经验,学习一门语言或框架时,请首先阅读官方文档。学习完毕后,再看其他相关文章(如本系列文章),才是正确的学习道路。
本专栏是对Flask官方文档中个人博客搭建进行的归纳总结,与官方文档结合事半功倍。基础薄弱的同学请戳Flask官方文档教程 本人经验,学习一门语言或框架时,请首先阅读官方文档。学习完毕后,再看其他相关文章(如本系列文章),才是正确的学习道路。 如果python都完全不熟悉,一定不要着急学习框架,请首先学习python官方文档,一步一个脚印。要不然从入门到放弃是大概率事件。 Python 官方文档教程
快到年末了 相信大家都在忙着处理年末数据 刚好有一个是对超市的商品库存进行分析的学员案例 真的非常简单~
一个简易的问答系统就这样完成了,当然,这个项目还可以进一步完善,比如 将数据存入Elasticsearch,通过它先进行初步的检索,然后再通过这个系统,当然我们也可以用其他的架构实现。如果你对这系统还有其他的疑问,也可以再下面进行留言!!!
#模版继承和页面之间的调用@app.route(&quot;/bl&quot;)def bl(): return render_template(&quot;file_2.html&quot;)主ht
#form表达提交@app.route(&quot;/data&quot;,methods=[&#39;GET&#39;,&#39;POST&#39;]) #methods 让当前路由支持GET 和
#form表达提交@app.route(&quot;/data&quot;,methods=[&#39;GET&#39;,&#39;POST&#39;]) #methods 让当前路由支持GET 和
#session 使用app.secret_key = &quot;dsada12212132dsad1232113&quot;app.config[&#39;PERMANENT_SESSION_LI
#文件上传@app.route(&quot;/file&quot;,methods=[&#39;GET&#39;,&#39;POST&#39;])def file(): if request.meth
#跳转操作:redirect@app.route(&quot;/red&quot;)def red(): return redirect(&quot;/login&quot;)
#session 使用app.secret_key = &quot;dsada12212132dsad1232113&quot;app.config[&#39;PERMANENT_SESSION_LI
@app.route(&quot;/req&quot;,methods=[&#39;GET&#39;,&#39;POST&#39;])def req(): print(request.headers)
#模版继承和页面之间的调用@app.route(&quot;/bl&quot;)def bl(): return render_template(&quot;file_2.html&quot;)主ht
#文件操作:send_file,支持图片 视频 mp3 文本等@app.route(&quot;/img&quot;)def img(): return send_file(&quot;1.jpg&q