-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.py
220 lines (195 loc) · 8 KB
/
app.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
import os
import sys
import click
from flask import Flask, render_template
from flask import request, url_for, redirect, flash
from flask_sqlalchemy import SQLAlchemy
from werkzeug.security import generate_password_hash, check_password_hash
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
#创建Flask应用实例
app = Flask(__name__)
#实例化扩展类
login_manager = LoginManager(app)
login_manager.login_view = 'login'
#创建数据库连接的URI,即数据库的位置
db_user = 'root'
db_password = '123456'
db_host = 'localhost'
db_name = 'moviedb'
# 使用 MySQL 数据库连接 URI
app.config['SQLALCHEMY_DATABASE_URI'] = f"mysql://{db_user}:{db_password}@{db_host}/{db_name}"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS']=False
app.config['SQLALCHEMY_DATABASE_CHARSET'] = 'utf8'
app.config['SECRET_KEY'] = 'dev'
db=SQLAlchemy(app)
#定义数据模型
class User(db.Model, UserMixin):
id=db.Column(db.Integer, primary_key=True) #主键
name=db.Column(db.String(20)) #名字
username = db.Column(db.String(20)) # 用户名
password_hash = db.Column(db.String(1024)) # 密码散列值
def set_password(self, password): # 用来设置密码的方法,接受密码作为参数
self.password_hash = generate_password_hash(password) #将生成的密码保持到对应字段
def validate_password(self, password): # 用于验证密码的方法,接受密码作为参数
return check_password_hash(self.password_hash, password)
class Movie(db.Model):
id=db.Column(db.Integer,primary_key=True) #主键
title=db.Column(db.String(60)) #电影标题
year=db.Column(db.String(4)) #电影年份
#自定义命令initdb,创建数据库
@app.cli.command() #注册为命令
@click.option('--drop', is_flag=True, help='Create after drop')
#设置选项
def initdb(drop):
if drop:
db.drop_all()
db.create_all()
click.echo('Initialized database.')
@app.cli.command()
def forge():
"""Generate fake data."""
db.create_all()
name = 'Amber'
movies = [
{'title': 'My Neighbor Totoro', 'year': '1988'},
{'title': 'Dead Poets Society', 'year': '1989'},
{'title': 'A Perfect World', 'year': '1993'},
{'title': 'Leon', 'year': '1994'},
{'title': 'Mahjong', 'year': '1996'},
{'title': 'Swallowtail Butterfly', 'year': '1996'},
{'title': 'King of Comedy', 'year': '1999'},
{'title': 'Devils on the Doorstep', 'year': '1999'},
{'title': 'WALL-E', 'year': '2008'},
{'title': 'The Pork of Music', 'year': '2012'},
]
user = User(name=name)
db.session.add(user)
for m in movies:
movie = Movie(title=m['title'], year=m['year'])
db.session.add(movie)
db.session.commit()
click.echo('Done.')
#上下文处理器
@app.context_processor
def inject_user():
user = User.query.first()
return dict(user=user)
#错误处理器
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
#主页
#增加数据
@app.route('/', methods=['GET','POST'])
def index():
if request.method == 'POST': # 判断是否是 POST 请求
if not current_user.is_authenticated: # 如果当前用户未认证
flash('You need to be logged in to create a new item.')
return redirect(url_for('index')) # 重定向到主页
# 获取表单数据
title = request.form.get('title')
year = request.form.get('year')
# 验证数据
if not title or not year or len(year) > 4 or len(title)> 60:
flash('Invalid input.') # 显示错误提示
return redirect(url_for('index')) # 重定向回主页
# 保存表单数据到数据库
movie = Movie(title=title, year=year) # 创建记录
db.session.add(movie) # 添加到数据库会话
db.session.commit() # 提交数据库会话
flash('Item created.') # 显示成功创建的提示
return redirect(url_for('index')) # 重定向回主页
user = User.query.first()
movies = Movie.query.all()
return render_template('index.html', user=user, movies=movies)
#编辑数据
@app.route('/movie/edit/<int:movie_id>', methods=['GET', 'POST'])
@login_required
def edit(movie_id):
movie = Movie.query.get_or_404(movie_id)
if request.method == 'POST': # 处理编辑表单的提交请求
title = request.form['title']
year = request.form['year']
if not title or not year or len(year) > 4 or len(title)> 60:
flash('Invalid input.')
return redirect(url_for('edit', movie_id=movie_id))# 重定向回对应的编辑页面
movie.title = title # 更新标题
movie.year = year # 更新年份
db.session.commit() # 提交数据库会话
flash('Item updated.')
return redirect(url_for('index')) # 重定向回主页
return render_template('edit.html', movie=movie) # 传入被编辑的电影记录
#删除数据
@app.route('/movie/delete/<int:movie_id>', methods=['POST']) #限定只接受 POST 请求
@login_required
def delete(movie_id):
movie = Movie.query.get_or_404(movie_id) # 获取电影记录
db.session.delete(movie) # 删除对应的记录
db.session.commit() # 提交数据库会话
flash('Item deleted.')
return redirect(url_for('index')) # 重定向回主页
# 生成账户
@app.cli.command()
@click.option('--username', prompt=True, help='The username usedto login.')
@click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True, help='The password used to login.')
def admin(username, password):
"""Create user."""
db.create_all()
user = User.query.first()
if user is not None:
click.echo('Updating user...')
user.username = username
user.set_password(password) # 设置密码
else:
click.echo('Creating user...')
user = User(username=username, name='Admin')
user.set_password(password) # 设置密码
db.session.add(user)
db.session.commit() # 提交数据库会话
click.echo('Done.')
#初始化flask-login
@login_manager.user_loader
def load_user(user_id): # 创建用户加载回调函数,接受用户 ID 作为参数
user = User.query.get(int(user_id)) # 用 ID 作为 User 模型的主键查询对应的用户
return user # 返回用户对象
#用户登录\登出
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
if not username or not password:
flash('Invalid input.')
return redirect(url_for('login'))
user = User.query.first()
# 验证用户名和密码是否一致
if username == user.username and user.validate_password(password):
login_user(user) # 登入用户
flash('Login success.')
return redirect(url_for('index')) # 重定向到主页
flash('Invalid username or password.') # 如果验证失败,显示错误消息
return redirect(url_for('login')) # 重定向回登录页面
return render_template('login.html')
@app.route('/logout')
@login_required # 用于视图保护,后面会详细介绍
def logout():
logout_user() # 登出用户
flash('Goodbye.')
return redirect(url_for('index')) # 重定向回首页
# 支持设置用户名字
@app.route('/settings', methods=['GET', 'POST'])
@login_required
def settings():
if request.method == 'POST':
name = request.form['name']
if not name or len(name) > 20:
flash('Invalid input.')
return redirect(url_for('settings'))
current_user.name = name
db.session.commit()
flash('Settings updated.')
return redirect(url_for('index'))
return render_template('settings.html')
# 对路由函数(@app.***)的理解:
# 在Flask中,使用@app.route()装饰器来定义路由。
# 这个装饰器告诉Flask应用程序,当访问指定的URL时,应该调用哪个Python函数。