python制作一个网页版的notebook,制作简易的网页记事本笔记本。 逃离我推掉我的手 2022-11-18 02:09 373阅读 0赞 # templates: # ## includes:(\_footer.html、\_formhelpers.html 、\_messages.html、\_navbar.html) ## ## \_footer.html ## <!-- Footer --> <footer class="page-footer font-small blue" style="margin-top: 50px"> <!-- Copyright --> <div class="footer-copyright text-center py-3"> Be made of K.kuimeng </div> <!-- Copyright --> </footer> <!-- Footer --> ## \_formhelpers.html ## {% macro render_field(field) %} { { field.label }} { { field(**kwargs)|safe }} {% if field.errors %} {% for error in field.errors %} <span class="help-inline text-danger">{ { error }}</span> {% endfor %} {% endif %} {% endmacro %} ## \_messages.html ## {% with messages = get_flashed_messages(with_categories=true) %} {% if messages %} {% for category, message in messages %} <div class="alert alert-{ { category }}">{ { message }}</div> {% endfor %} {% endif %} {% endwith %} {% if error %} <div class="alert alert-danger">{ {error}}</div> {% endif %} {% if msg %} <div class="alert alert-success">{ {msg}}</div> {% endif %} ## \_navbar.html ## <nav class="navbar navbar-default"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="/">学习笔记</a> </div> <div id="navbar" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li><a href="/">主页</a></li> <li><a href="/articles">笔记</a></li> <li><a href="/about">关于</a></li> </ul> <ul class="nav navbar-nav navbar-right"> {% if session.logged_in %} <li><a href="/dashboard">控制台</a></li> <li><a href="/logout">退出</a></li> {% else %} <li><a href="/register">注册</a></li> <li><a href="/login">登录</a></li> {% endif %} </ul> </div><!--/.nav-collapse --> </div> </nav> ## about.html ## {% extends 'layout.html' %} {% block body %} <div class="content"> <h1 class="title-center">关于我们</h1> <p> K.kuimeng </p> </div> {% endblock %} ## add\_article.html ## {% extends 'layout.html' %} {% block body %} <h1>添加笔记</h1> {% from "includes/_formhelpers.html" import render_field %} <form method="POST" action=""> <div class="form-group"> { { render_field(form.title, class_="form-control") }} </div> <div class="form-group"> { { render_field(form.content, class_="form-control content-text", id="editor") }} </div> <p><input class="btn btn-primary" type="submit" value="提交"> </form> <script src="//cdn.ckeditor.com/4.11.2/standard/ckeditor.js"></script> <script type="text/javascript"> CKEDITOR.replace( 'editor') </script> {% endblock %} ## article.html ## {% extends 'layout.html' %} {% block body %} <div class="content"> <h1 class="title-center">{ {article.title}}</h1> <p class="post-author fr">作者:{ {article.author}}   { {article.create_date}} </p> <div class="clear"> { {article.content | safe}} </div> </div> {% endblock %} ## articles.html ## {% extends 'layout.html' %} {% block body %} <div class="content"> <!-- Home Post List --> {% for article in articles %} <article class="post-preview"> <h1 class="post-title">{ {article.title}}</h1> <p class="post-author">作者:{ {article.author}}   { {article.create_date}} </p> <p>{ {article.content | safe | truncate(200, True)}}</p> <div class="clearfix"> <a class="btn btn-primary float-right" href="article/{ {article.id}}">查看全文 →</a> </div> </article> <hr class="underline"> {% endfor %} </div> {% endblock %} ## dashboard.html ## {% extends 'layout.html' %} {% block body %} <h1>控制台 <small> 欢迎 { {session.username}}</small></h1> <a class="btn btn-success" href="/add_article"> 添加笔记 </a> <hr> <table class="table table-striped"> <tr> <th>ID</th> <th>标题</th> <th>作者</th> <th>日期</th> <th></th> <th></th> </tr> {% for article in articles %} <tr> <td>{ {article.id}}</td> <td>{ {article.title}}</td> <td>{ {article.author}}</td> <td>{ {article.create_date}}</td> <td><a href="edit_article/{ {article.id}}" class="btn btn-default pull-right">Edit</a></td> <td> <form action="{ {url_for('delete_article', id=article.id)}}" method="post"> <input type="hidden" name="_method" value="DELETE"> <input type="submit" value="Delete" class="btn btn-danger"> </form> </td> </tr> {% endfor %} </table> {% endblock %} ## edit\_article.html ## {% extends 'layout.html' %} {% block body %} <h1>编辑笔记</h1> {% from "includes/_formhelpers.html" import render_field %} <form method="POST" action=""> <div class="form-group"> { { render_field(form.title, class_="form-control") }} </div> <div class="form-group"> { { render_field(form.content, class_="form-control", id="editor") }} </div> <p><input class="btn btn-primary" type="submit" value="Submit"> </form> <script src="//cdn.ckeditor.com/4.11.2/standard/ckeditor.js"></script> <script type="text/javascript"> CKEDITOR.replace('editor') </script> {% endblock %} ## home.html ## {% extends 'layout.html' %} {% block body %} <div class="jumbotron text-center"> <h1>欢迎使用读书笔记</h1> <p class="lead">好记性不如烂笔头!</p> {% if session.logged_in == NULL %} <a href="/register" class="btn btn-primary btn-lg">注册</a> <a href="/login" class="btn btn-success btn-lg">登录</a> {% endif %} </div> {% endblock %} ## layout.html ## <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>读书笔记</title> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="/static/css/style.css"> <script src="https://cdn.bootcss.com/jquery/2.2.4/jquery.js"></script> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> </head> <body> {% include 'includes/_navbar.html' %} <div class="container"> {% include 'includes/_messages.html' %} {% block body %}{% endblock %} </div> {% include 'includes/_footer.html' %} </body> </html> ## login.html ## {% extends 'layout.html' %} {% block body %} <div class="content"> <h1 class="title-center">用户登录</h1> <form action="" method="POST" onsubmit="return checkLogin()"> <div class="form-group"> <label>用户名</label> <input type="text" name="username" class="form-control" value={ {request.form.username}}> </div> <div class="form-group"> <label>密码</label> <input type="password" name="password" class="form-control" value=""> </div> <button type="submit" class="btn btn-primary">登录</button> </form> </div> <script> function checkLogin(){ var username = $("input[name='username']").val() var password = $("input[name='password']").val() // 检测用户名长度 if ( username.length < 2 || username.length > 25){ alert('用户名长度在2-25个字符之间') return false; } // 检测密码长度 if ( username.length < 2 || username.length > 25){ alert('密码长度在6-20个字符之间') return false; } } </script> {% endblock %} ## register.html ## {% extends 'layout.html' %} {% block body %} <div class="content"> <h1 class="title-center">用户注册</h1> {% from "includes/_formhelpers.html" import render_field %} <form method="POST" action=""> <div class="form-group"> { {render_field(form.email, class_="form-control")}} </div> <div class="form-group"> { {render_field(form.username, class_="form-control")}} </div> <div class="form-group"> { {render_field(form.password, class_="form-control")}} </div> <div class="form-group"> { {render_field(form.confirm, class_="form-control")}} </div> <p><input type="submit" class="btn btn-primary" value="注册"></p> </form> </div> {% endblock %} ## forms.py ## from wtforms import Form, StringField, TextAreaField, PasswordField from wtforms.validators import DataRequired,Length,Email,EqualTo # Article Form Class class ArticleForm(Form): title = StringField( '标题', validators=[ # DataRequired(message= '标题长度应该在2-30字符之间'), DataRequired(message='长度不少于5个字符'), Length(min=2,max=30) ] ) content = TextAreaField( '内容', validators=[ DataRequired(message='长度不少于5个字符'), Length(min=5) ] ) # Register Form Class class RegisterForm(Form): username = StringField( '用户名', validators=[ DataRequired(message='请输入用户名'), Length(min=2, max=25,message='长度在2-25个字符之间') ] ) email = StringField( '邮箱', validators = [ DataRequired(message="请输入邮箱"), Email(message='请输入正确的邮箱格式') ] ) password = PasswordField( '密码', validators = [ DataRequired(message='密码不能为空'), Length(min=6,max=20,message='长度在6-20个字符之间'), ] ) confirm = PasswordField( '确认密码', validators=[ DataRequired(message='密码不能为空'), Length(min=6, max=20), EqualTo('password', message='2次输入密码不一致') ] ) ## manage.py ## from flask import Flask, render_template, flash, redirect, url_for, session, request, logging from mysql_util import MysqlUtil from passlib.hash import sha256_crypt from functools import wraps import time from forms import RegisterForm,ArticleForm app = Flask(__name__) # 创建应用 # 首页 @app.route('/') def index(): return render_template('home.html') # 渲染模板 # 关于我们 @app.route('/about') def about(): return render_template('about.html') # 渲染模板 # 笔记列表 @app.route('/articles') def articles(): db = MysqlUtil() # 实例化数据库操作类 sql = 'SELECT * FROM articles ORDER BY create_date DESC LIMIT 5' # 从article表中筛选5条数据,并根据日期降序排序 articles = db.fetchall(sql) # 获取多条记录 if articles : # 如果存在,遍历数据 return render_template('articles.html', articles=articles) # 渲染模板 else: # 如果不存在,提示“暂无笔记” msg = '暂无笔记' return render_template('articles.html', msg=msg) # 渲染模板 # 笔记详情 @app.route('/article/<string:id>/') def article(id): db = MysqlUtil() # 实例化数据库操作类 sql = "SELECT * FROM articles WHERE id = '%s'" % (id) # 根据ID查找笔记 article = db.fetchone(sql) # 获取一条记录 return render_template('article.html', article=article) # 渲染模板 # 用户注册 @app.route('/register', methods=['GET', 'POST']) def register(): form = RegisterForm(request.form) # 实例化表单类 if request.method == 'POST' and form.validate(): # 如果提交表单,并字段验证通过 # 获取字段内容 email = form.email.data username = form.username.data password = sha256_crypt.encrypt(str(form.password.data)) # 对密码进行加密 db = MysqlUtil() # 实例化数据库操作类 sql = "INSERT INTO users(email,username,password) \ VALUES ('%s', '%s', '%s')" % (email,username,password) # user表中插入记录 db.insert(sql) flash('您已注册成功,请先登录', 'success') # 闪存信息 return redirect(url_for('login')) # 跳转到登录页面 return render_template('register.html', form=form) # 渲染模板 # 用户登录 @app.route('/login', methods=['GET', 'POST']) def login(): if "logged_in" in session: # 如果已经登录,则直接跳转到控制台 return redirect(url_for("dashboard")) if request.method == 'POST': # 如果提交表单 # 从表单中获取字段 username = request.form['username'] password_candidate = request.form['password'] sql = "SELECT * FROM users WHERE username = '%s'" % (username) # 根据用户名查找user表中记录 db = MysqlUtil() # 实例化数据库操作类 result = db.fetchone(sql) # 获取一条记录 if result : # 如果查到记录 password = result['password'] # 用户填写的密码 # 对比用户填写的密码和数据库中记录密码是否一致 if sha256_crypt.verify(password_candidate, password): # 调用verify方法验证,如果为真,验证通过 # 写入session session['logged_in'] = True session['username'] = username flash('登录成功!', 'success') # 闪存信息 return redirect(url_for('dashboard')) # 跳转到控制台 else: # 如果密码错误 error = '用户名和密码不匹配' return render_template('login.html', error=error) # 跳转到登录页,并提示错误信息 else: error = '用户名不存在' return render_template('login.html', error=error) return render_template('login.html') # 如果用户已经登录 def is_logged_in(f): @wraps(f) def wrap(*args, **kwargs): if 'logged_in' in session: # 判断用户是否登录 return f(*args, **kwargs) # 如果登录,继续执行被装饰的函数 else: # 如果没有登录,提示无权访问 flash('无权访问,请先登录', 'danger') return redirect(url_for('login')) return wrap # 退出 @app.route('/logout') @is_logged_in def logout(): session.clear() flash('您已成功退出', 'success') # 闪存信息 return redirect(url_for('login')) # 跳转到登录页面 # 控制台 @app.route('/dashboard') @is_logged_in def dashboard(): db = MysqlUtil() # 实例化数据库操作类 sql = "SELECT * FROM articles WHERE author = '%s' ORDER BY create_date DESC" % (session['username']) # 根据用户名查找用户笔记信息 result = db.fetchall(sql) # 查找所有笔记 if result: # 如果笔记存在,赋值给articles变量 return render_template('dashboard.html', articles=result) else: # 如果笔记不存在,提示暂无笔记 msg = '暂无笔记信息' return render_template('dashboard.html', msg=msg) # 添加笔记 @app.route('/add_article', methods=['GET', 'POST']) @is_logged_in def add_article(): form = ArticleForm(request.form) # 实例化ArticleForm表单类 if request.method == 'POST' and form.validate(): # 如果用户提交表单,并且表单验证通过 # 获取表单字段内容 title = form.title.data content = form.content.data author = session['username'] create_date = time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) db = MysqlUtil() # 实例化数据库操作类 sql = "INSERT INTO articles(title,content,author,create_date) \ VALUES ('%s', '%s', '%s','%s')" % (title,content,author,create_date) # 插入数据的SQL语句 db.insert(sql) flash('创建成功', 'success') # 闪存信息 return redirect(url_for('dashboard')) # 跳转到控制台 return render_template('add_article.html', form=form) # 渲染模板 # 编辑笔记 @app.route('/edit_article/<string:id>', methods=['GET', 'POST']) @is_logged_in def edit_article(id): db = MysqlUtil() # 实例化数据库操作类 fetch_sql = "SELECT * FROM articles WHERE id = '%s' and author = '%s'" % (id,session['username']) # 根据笔记ID查找笔记信息 article = db.fetchone(fetch_sql) # 查找一条记录 # 检测笔记不存在的情况 if not article: flash('ID错误', 'danger') # 闪存信息 return redirect(url_for('dashboard')) # 获取表单 form = ArticleForm(request.form) if request.method == 'POST' and form.validate(): # 如果用户提交表单,并且表单验证通过 # 获取表单字段内容 title = request.form['title'] content = request.form['content'] update_sql = "UPDATE articles SET title='%s', content='%s' WHERE id='%s' and author = '%s'" % (title, content, id,session['username']) db = MysqlUtil() # 实例化数据库操作类 db.update(update_sql) # 更新数据的SQL语句 flash('更改成功', 'success') # 闪存信息 return redirect(url_for('dashboard')) # 跳转到控制台 # 从数据库中获取表单字段的值 form.title.data = article['title'] form.content.data = article['content'] return render_template('edit_article.html', form=form) # 渲染模板 # 删除笔记 @app.route('/delete_article/<string:id>', methods=['POST']) @is_logged_in def delete_article(id): db = MysqlUtil() # 实例化数据库操作类 sql = "DELETE FROM articles WHERE id = '%s' and author = '%s'" % (id,session['username']) # 执行删除笔记的SQL语句 db.delete(sql) # 删除数据库 flash('删除成功', 'success') # 闪存信息 return redirect(url_for('dashboard')) # 跳转到控制台 if __name__ == '__main__': app.secret_key='secret123' app.run(debug=True) ## mysql\_util.py ## import pymysql # 引入pymysql模块 import traceback # 引入python中的traceback模块,跟踪错误 import sys # 引入sys模块 class MysqlUtil(): def __init__(self): ''' 初始化方法,连接数据库 ''' host = 'localhost' # 主机名 user = 'root' # 数据库用户名 password = '******' # 数据库密码 database = 'notebook' # 数据库名称 self.db = pymysql.connect(host=host,user=user,password=password,db=database) # 建立连接 self.cursor = self.db.cursor(cursor=pymysql.cursors.DictCursor) # 设置游标,并将游标设置为字典类型 def insert(self, sql): ''' 插入数据库 sql:插入数据库的sql语句 ''' try: # 执行sql语句 self.cursor.execute(sql) # 提交到数据库执行 self.db.commit() except Exception: # 方法一:捕获所有异常 # 如果发生异常,则回滚 print("发生异常", Exception) self.db.rollback() finally: # 最终关闭数据库连接 self.db.close() def fetchone(self, sql): ''' 查询数据库:单个结果集 fetchone(): 该方法获取下一个查询结果集。结果集是一个对象 ''' try: # 执行sql语句 self.cursor.execute(sql) result = self.cursor.fetchone() except: # 方法二:采用traceback模块查看异常 # 输出异常信息 traceback.print_exc() # 如果发生异常,则回滚 self.db.rollback() finally: # 最终关闭数据库连接 self.db.close() return result def fetchall(self, sql): ''' 查询数据库:多个结果集 fetchall(): 接收全部的返回结果行. ''' try: # 执行sql语句 self.cursor.execute(sql) results = self.cursor.fetchall() except: # 方法三:采用sys模块回溯最后的异常 # 输出异常信息 info = sys.exc_info() print(info[0], ":", info[1]) # 如果发生异常,则回滚 self.db.rollback() finally: # 最终关闭数据库连接 self.db.close() return results def delete(self, sql): ''' 删除结果集 ''' try: # 执行sql语句 self.cursor.execute(sql) self.db.commit() except: # 把这些异常保存到一个日志文件中,来分析这些异常 # 将错误日志输入到目录文件中 f = open("\log.txt", 'a') traceback.print_exc(file=f) f.flush() f.close() # 如果发生异常,则回滚 self.db.rollback() finally: # 最终关闭数据库连接 self.db.close() def update(self, sql): ''' 更新结果集 ''' try: # 执行sql语句 self.cursor.execute(sql) self.db.commit() except: # 如果发生异常,则回滚 self.db.rollback() finally: # 最终关闭数据库连接 self.db.close() ## style.css ## p { margin: 0 0 10px; font-size: 18px; } a:link { text-decoration:none;} .content { padding:10px 20px; background-color: #eee; } .title-center { text-align: center; } .post-author { color:#999;font-style: italic;font-size: 18px; } .underline { border-top: 1px solid gray; } .fr { float:right } .fl { float:left } .clear { clear:both; } ## notebook.sql ## -- ---------------------------- -- Table structure for articles -- ---------------------------- DROP TABLE IF EXISTS `articles`; CREATE TABLE `articles` ( `id` int(8) NOT NULL AUTO_INCREMENT, `title` varchar(255) DEFAULT NULL, `content` text, `author` varchar(255) DEFAULT NULL, `create_date` datetime DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of articles -- ---------------------------- -- ---------------------------- -- Table structure for users -- ---------------------------- DROP TABLE IF EXISTS `users`; CREATE TABLE `users` ( `id` int(8) NOT NULL AUTO_INCREMENT, `username` varchar(255) DEFAULT NULL, `email` varchar(255) DEFAULT NULL, `password` varchar(255) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; -- ---------------------------- -- Records of users -- ---------------------------- ## requirements.txt ## Click==7.0 Flask==1.0.2 itsdangerous==1.1.0 Jinja2==2.10 MarkupSafe==1.1.0 passlib==1.7.4 PyMySQL==0.9.3 Werkzeug==0.14.1 WTForms==2.2.1
还没有评论,来说两句吧...