Skip to content

Heaveniost/Flask_Web

Repository files navigation

Flasky


This repository contains the source code learning from Flask Web Development.


第一部分 Flask简介

第一章:安装
第二章:程序的基本结构
第三章:模板
第四章:Web表单
第五章:数据库
第六章:电子邮件
第七章:大型程序的结构

第二部分 实例:社交博客程序

第八章:用户认证
第九章:用户角色
第十章:用户资料
第十一章:博客文章
第十二章:关注者
第十三章:用户评论
第十四章:应用编程接口

第三部分 成功在望

第十五章:测试
第十六章:性能
第十七章:部署
第十八章:简介


写在前面:


6.16

根据教程完成的web小应用 已成功部署到Heroku服务器上,可直接访问 My Flask_web
注册用户需验证邮箱,如果嫌麻烦,可以使用账户 276434920@qq.com 密码 123 登陆体验
通过这个教程 算是了解了一下利用Flask开发web application的流程


6.19

前两天端午,自己也偷偷休息了下,没做什么有效的工作
今天将笔记整理了下,上传到GitHub,网站肯定需要润色,但接下来准备找工作,所以可能需要对之前所学的知识点做个总结,再刷一些面试题,之后等工作落定后再修改网站,添加一些新的功能

单词缩写

Pro:Problem
Sol:Solution
Sum:Summary
Ps:Postscript
Rs:Reference source

每日记录:


6.7


Pro:

(venv) Mac:Flask_Web Heaven$ export FLASKY_ADMIN = <304090717@qq.com>  
报错:bash: syntax error near unexpected token 'newline'  脚本中有特殊字符?

Sol:去掉<> 将邮箱用''括起来

Mac中的环境变量

Tip:Mac 系统,如果只在终端使用 export 这个命令写入环境变量,它配置的只是临时变量,不能长期保存,电脑开关机后或重新打开终端或另开一个窗口,仍然会回到没有配置环境变量的状态。

etc/profile 系统级别的环境变量
~/.bash_profile 用户级别的环境变量 

导入变量:export MAIL_USERNAME=30490717@qq.com 
查看变量:echo $MAIL_USERNAME 

flask-mail的邮箱配置问题

配置邮箱发送邮件需要去设置里开启邮箱的SMTP服务,并通过短信获取服务提供商的发送的密码,此处使用了QQ邮箱
新密码:`ahvtapoodpuscafb` (不再是QQ原始密码)(Ps:此密码已修改,最新密码保存在本地主机中)

linux 怎么将一个文件夹移入另一个文件夹

mv old_directory/ new_directory/ 没有指定具体值则将此文件夹一起移走

Tip:@property装饰器把方法变成属性便于调用

6.8


Pro: python manage.py runserver 提示:ValueError: urls must start with a leading slash

Sol:路由注册时忘记加上'/‘ url_prefix='/auth'而不是'auth'

Pro: 启动程序后 报错 email = db.column(db.String(64), unique=True, index=True) TypeError: column() got an unexpected keyword argument 'unique'

Sol:不太清楚错误产生的原因 暂时先去掉了unique 和 index 两项
之后在 Python shell 中新建用户
	email='heaven@gmail.com',username='Heaven',password='123')
	email='zero@123.com',username='zero',password='123'
提示 AttributeError: 'String' object has no attribute 'lower'
回到代码中查看,发现 column拼错了
一个小小的错误 可能就是几个小时的时间 仔细看看 column-->Column 还有这个逗号,也错了

Pro: 在Python shell中新建用户提示没有users这张表

InvalidRequestError: This Session's trSolaction has been rolled back due to a previous exception during flush. To begin a new trSolaction with this Session, first issue Session.rollback(). Original exception was: (sqlite3.OperationalError) no such table: users [SQL: u'INSERT INTO users (username, role_id, password_hash) VALUES (?, ?, ?)'] [parameters: ('Heaven', None, 'pbkdf2:sha256:50000$1lXaaAUe$26d3996391b079f20df978dd6b944516df194115760d939ef2e55e1ea2f29977')] (Background on this error at: http://sqlalche.me/e/e3q8)

Sol:python manage.py db upgrade 需要将数据库更新到最新版本
有时直接使用无效 还需要创建迁移脚本 python manage.py db migrate -m"initila migration"
Ps: 正确步骤时每次对模型(models.py)修改后 都要创建迁移脚本 然后将数据库更新到最新版本

从昨晚到现在 花了五六个小时 主要解决了两个错误 一是拼写引起的错误 二是没有创建数据库迁移脚本


Pro: 使用 git status 时出现 Untracked files 和 Modified,这两种状态有什么区别

Sol:Untracked files 有可能是新增的文件 之前没有记录 所以是未跟踪
	 Modified 则是之前有记录的文件,做了修改

6.9


Tip:单元测试 python manage.py test 可以发现程序中的一些错误


Pro: 创建迁移脚本时提示 py:69: UserWarning: Skipping unsupported ALTER for creation of implicit constraint warnings.warn(msg)

Sol:中间不小心误删了一个迁移数据库(导致后面部署到服务器时还出现了问题)此时误删的文件没有补上 但数据库模型已成功创建

Pro: 运行程序 python manage.py runserver,提示

raise SMTPAuthenticationError(code, resp)

SMTPAuthenticationError: (535, 'Error:\xc7\xeb\xca\xb9\xd3\xc3\xca\xda\xc8\xa8\xc2\xeb\xb5\xc7\xc2\xbc\xa1\xa3\xcf\xea\xc7\xe9\xc7\xeb\xbf\xb4: http://service.mail.qq.com/cgi-bin/help?subtype=1&&id=28&&no=1001256')

Sol

一直没有成功发送邮件 想了半天 环境变量也没问题 多次设置了 也去shell里尝试发送邮件 还是不行 猜测有两种原因 一是在手机上点了什么服务器之类的 使授权码失效 二是配置环境变量的时候没加'' 直接输入的字符串 猜测是第二种原因,重新设置环境变量,问题解决  

Sum:出现问题要学会分析  一是看shell报什么错 从前往后看 最后一句都给了一个网站 登陆这个网站看看什么原因 就知道是授权码的问题 二是熟悉了python manage.py shell的使用 对这个shell的作用有了一定的了解  比如输入os.environ.get('MAIL_PASSWORD') 可以得到一个值 

6.10


Pro: 发现作者在 修改密码 这一步没有验证新密码是否和原密码相同这个问题 自己加上了这部分代码

Pro: 为什么有两个resetpassword表单


Pro: 单元测试 python manage.py test 报错 BuildError: Could not build url for endpoint 'auth.password_reset, token=token, _external=True'. Did you mean 'auth.password_reset_request' instead?

Sol:找到源代码

if not current_user.is_anonymous:
	return redirect(url_for('main.index'))

没发现错误,继续查找

<p>To reset your password <a href="{{ url_for('auth.password_reset', token=token, _external=True) }}">click here</a></p>  

发现问题

</a>.</p> 这中间有个点没加上 一直找不到正确的链接 -Ps:这个点的作用还没查到

6.11


Pro:last_seen 上次登录时间 这个功能是怎么实现的


Pro: 运行程序报错 AttributeError: 'SQLAlchemy' object has no attribute 'Datetime'

Sol:Datetime --> DateTime

Pro: 添加了头像,但一直报错 NameError: global name 'dafault' is not defined 可能是拼错了? python manage.py test 也报错

Sol:将identicon 改成 retro 测试没问题了
再改回来 几个头像都能正常显示 除了blank
虽然默认值一样 但不同邮箱的头像是不一样的

Pro:

<div class="post-thumbnail">
		<a href="{{ url_for('.user', username=post.author.username) }}"></a>
</div>
定义这个块有什么作用啊

Pro: AttributeError: 'AnonymousUserMixin' object has no attribute 'can'

Sol:login_manager.anonymous_user = AnonymousUser 这句话漏掉了,新登录用户默认为匿名用户

Pro: 头像跟数据一直重叠在一起 无法正常显示

Sol:换个浏览器就好了  一直在检查 花了一两个小时 还是没检查出原因 后来搜索别人做这一部分,也遇到了这样的问题,给出了方案
通过搜索 
	min-height: 260px; 	
	margin-left:280px 
找到了别人跟着教程做的记录 最终发现了原因所在

6.12


Sum:

Profile pages 资料页
blog posts 博客文章
独立模板 局部模板 _posts.html

Pro: main/ auth/ 为什么需要两个文件夹


Pro: 生成虚拟用户和文章

User.generate_fake(100)
Post.generate_fake(100)

第一句话没问题 运行Post.generate_fake(100)报错

OperationalError: (sqli te3.OperationalError) database is locked [SQL: u'INSERT INTO posts (body, timestamp, author_id) VALUES (?, ?, ?)'] [parameters: (u'Sed sagittis.', '2018-06-10 00:00:00.000000', 66)] (Background on this error at: http://sqlalche.me/e/e3q8)

Sol:

登陆提供的网站 at: http://sqlalche.me/e/e3q8 查找原因

查到函数
@staticmethod
def generate_fake(count=10):
    from random import seed, randint
    import forgery_py
    from sqlalchemy.exc import IntegrityError

    seed()
    user_count = User.query.count()
    u = User.query.offset(5).first()
    p = Post(body=forgery_py.lorem_ipsum.sentence(),timestamp=forgery_py.date.date(True), author=u)
    db.session.add(p)
    db.session.commit()

搞了半天不清楚什么原因 然后运行 User.generate_fake(100)也报错 猜测是之前运行这个命令时 有某个进程在数据库没有正常关闭 重启命令行 再运行就OK了  我服  花我一个多小时

Pro:

<span class="label lable-primary">Edit</span>
<span class="label label-danger">Edit [Admin]</span>
<span class="label label-default">Permalink</span>
这几行代码间的 lable-primary label-danger label-default有什么区别

Sol:label表示标签 传入不同的参数标签的样式会有所不同 第二个一直没正确显示 是因为label拼成了lable 

6.13


Pro: 测试报错

FAIL: test_follows (test_user_model.UserModelTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
	File "/Users/Heaven/Flask_Web/tests/test_user_model.py", line 156, in test_follows
	self.assertFalse(u1.is_following(u2))
	AssertionError: True is not false

Sol:first()  把括号弄丢了

Pro: 运行程序后 报错 TypeError: 'str' object is not callable

Sol:

比较了一遍代码 没发现错误 重启termainal browser 没用 运行python manage.py shell 

>>>from .models import Comment
>>>m = Comment()
>>>m.body = 'dd'        -->TypeError: 'str' object is not callable
>>>m.body_text = 'ff'   -->正常

去查看models 发现了问题 db.event.listen('Comment.body, 'set', 'Comment.on_changed_body') 
多了一个引号 去掉引号 问题解决

Pro: ImportError: No module named api_1_0

Sol:

试了 .api_1_0  api_1_0  app.api_1_0 都没解决问题
api_1_0 里的初始化文件写成了 __init.py 漏了下滑线 改为 __init__.py 问题解决

Pro: 在browser中运行 http --json --auth '304090717@qq.com':'123' GET http://127.0.0.1:5000/api/v1.0/posts 报错:

http: error: ConnectionError: HTTPConnectionPool(host='127.0.0.1', port=5000): Max retries exceeded with url: /api/v1.0/posts (Caused by NewConnectionError('<urllib3.connection.HTTPConnection object at 0x109364190>: Failed to establish a new connection: [Errno 61] Connection refused',)) while doing GET request to URL: http://127.0.0.1:5000/api/v1.0/posts

Sol:

HTTPie测试web服务时,要先启动服务器,再执行执行测试代码。打开两个cmd界面就行了。

Sum: api_1_0 中没有views.py 是怎么处理路由的 不是根据文件夹 虽然把相同前缀的路由都放在同个文件夹中 但是出路路由还是根据 @api.route('../..') 这个路径来的 跟文件夹没关系(尝试把 posts/ 放在 users/ 文件夹中 还是能够正常处理) 所以处理路由是在文件夹中逐个寻找的 

Pro: 运行 http://localhost:5000/api/v1.0/posts/55 报错 TypeError: <bound method Post.to_json of <Post 55>> is not JSON serializable 报的不是 Not Found 这个错 说明此文章存在 是代码出错

Sol:return jsonify(post.to_json) 中json()括号掉了

**Pro:**试了下几个路由 都能走通 目前不太理解这个API是干嘛用的? 是不是比如main下的路由通过web browser浏览的路由 通过手机端或平板端浏览需要进行一些处理 这个API就是处理的那部分过程 比如客户端只接受JSON 不接受HTML作为响应 就把响应全部处理程JSON对象再返还给用户?


Pro: 进行测试 python manage.py test

报错:
test_404 (test_api.APITestCase) ... ok
test_anonymous (test_api.APITestCase) ... ERROR
test_bad_auth (test_api.APITestCase) ... ok
test_comments (test_api.APITestCase) ... ERROR
test_no_auth (test_api.APITestCase) ... ERROR
test_posts (test_api.APITestCase) ... FAIL
test_token_auth (test_api.APITestCase) ... ERROR
test_unconfirmed_account (test_api.APITestCase) ... ok
test_users (test_api.APITestCase) ... ok

Sol:

逐个分析 逐个解决
test_anonymous (test_api.APITestCase) ... ERROR
TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.


test_comments (test_api.APITestCase) ... ERROR
AttributeError: type object 'Comment' has no attribute 'from_json'
这个方法漏写了 补上 ok


test_posts (test_api.APITestCase) ... FAIL
Traceback (most recent call last):
	File "/Users/Heaven/Flask_Web/tests/test_api.py", line 125, in test_posts
	self.assertTrue(response.status_code == 400)
	AssertionError: False is not true	
换成401 403 404 都不行 加上一句 print response.status_code 结果为201 正确插入了 研究代码 发现
	if body is None and body == '':
		raise ValidationError('post does not have a body')
此处应该是 or 而非 and 


File "/Users/Heaven/Flask_Web/app/api_1_0/posts.py", line 49, in edit_post
	if f.current_user != post.author and not g.current_user.can(Permission.ADMINISTER):
NameError: global name 'f' is not defined
找到源代码 发现有问题的语句
if f.current_user != post.author and not g.current_user.can(Permission.ADMINISTER): 
将 f 改成 g   ok 


test_token_auth (test_api.APITestCase) ... ERROR
	File "/Users/Heaven/Flask_Web/app/api_1_0/authentication.py", line 15, in verify_password
	g.current_user = User.verify_password(email_or_token)
TypeError: unbound method verify_password() must be called with User instance as first argument (got str instance instead)
找到有问题的语句
User.verify_password(email_or_token) --> User.verify_auth_password(email_or_token)


修改完成后 这三个错误都变成了
 TypeError: The view function did not return a valid response. The function either returned None or ended without a return statement.

经分析 找到 def get_posts():
	if pagination.has_next:
这里出现了两个错误 一个是 if 语句的缩进出现了问题 二是拼写出了问题  
将next-->prev 原因是两个函数都定义成了有下一页然后返回值 有一个应该是有上一页则返回值

Sum:这些错误都是拼写的错误 真的要注意


Pro: 加入 selenium 后进行测试提示 test_admin_home_page (test_selenium.SeleniumTestCase) ... skipped 'Web browser not available'

Sol:

是因为默认的是firefox() 无法启动 直接将 webdriver.Firefox()-->webdriver.Chrome()/Safari() 是没有用的 因为火狐浏览器原生支持selenium  Chrome 和 Safari 需要下载插件和配置

去 http://npm.taobao.org/mirrors/chromedriver/ 下载最新版的chromedriver 解压后移入/usr/local/bin 不要写入/usr/bin(此操作也不允许 管理员权限都不行 也没必要)

Pro: 运行程序后报错

Traceback (most recent call last):
	File "/Users/Heaven/Flask_Web/tests/test_selenium.py", line 78, in test_admin_home_page
	self.client.page_source))  Hello,\s+Stranger'
	AssertionError: None is not true

Sol:

正则没匹配上对应的字符 所以返回 None 去掉'!'即可  跟'+'没关系 

Sum:

为了实现数据库中多对多的关系,需要引入第三个表--关联表,通过SQLAlchemgy可以很快速的生成关联表,但是不能自定义添加额外的信息(如日期),所以必须提升关联表的地位,使其变成程序可以放访问的模型

@property装饰器 把一个方法变成属性 可以做一些限制 又方便调用 

加上selenium 进行测试 服务器线程总是不能正常关闭 都是手动 kill 

url_for('.index',name=name,_external=True) 	传入视图函数名作为参数返回url,将动态部分作为关键字参数,第三个参数表示绝对地址

disabled的值表示能否正常显示评论内容 设为True后就不能显示评论内容了 moderator的作用就是禁止一些评论 而非修改评论 

json.dumps()将Python dict对象序列化为json对象 
json.loads()将json对象序列化为dict 对象 
如果是对文件进行操作 去掉s
Serializer() 序列化
jsonify() 使用了json.dumps()方法 返回的格式为 application/json
json.dumps() 返回的content-type: text/html; 

性能
缓慢数据库查询 部署在生产环境中 默认是在开发环境 需要修改 记录保存在日志中 需要配置日志记录器
分析源码 由于会影响程序运行的时间 配置在开发阶段
python manage.py profile 启动服务 并检测各部分程序运行时间

@staticmethod python 内置函数 让类不用实例化就可以调用该方法
	class a:
		@staticmethod
		def f():
			pass
可以使用 a.f()来调用 也可 a().f() 

Tip:在 heroku.com 注册账号验证码一直不出来 需要翻墙 才能刷新验证码 邮箱最好用gmail


6.15/6.16


Sum:购买了一些工具,成功注册Heroku账号,此处不提


Pro: 将实例代码推送到heroku上时,提示

remote: -----> Installing python-3.6.5
remote: -----> Installing pip
自动下载了一些包 这个是怎么实现的

Sol:

根据需求文件requirems来实现的,自动安装其中提到的包

Pro: 运行 pipenv install 报错: raise ValueError('unknown locale: %s' % localename) ValueError: unknown locale: UTF-8

Sol: 在 ~./bash_profile 加入

export LC_ALL=en_US.UTF-8
export LANG=en_US.UTF-8

然后重新打开要运行 pipenv install这个命令的窗口 运行即可


Pro: 先下载 Postgres 安装 然后在命令行输入

sudo mkdir -p /etc/paths.d && echo /Applications/Postgres.app/Contents/Versions/latest/bin | sudo tee /etc/paths.d/postgresapp

连接数据库
Mac:python-getting-started Heaven$ psql -h localhost
psql: could not connect to server: Connection refused
	Is the server running on host "localhost" (127.0.0.1) and accepting
	TCP/IP connections on port 5432?
could not connect to server: Connection refused
	Is the server running on host "localhost" (::1) and accepting
	TCP/IP connections on port 5432?

Sol:

PostgreSQL 服务未启动 进入 /Applications/Postgres.app/Contents/Versions/10/bin/psql 启动服务器

Tip:

Procfile 文件规定了用哪个命令启动程序  
Created postgresql-concentric-49098 as DATABASE_URL 设置数据库链接

Pro: git push heroku master 后创建的是python3.6 会有影响不 本地是2.7 Sol: 有影响,后面会提到


Pro: 运行 heroku run python manage.py deploy 报错:

sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: users
[SQL: 'ALTER TABLE users ADD COLUMN email VARCHAR(64)'] (Background on this error at: http://sqlalche.me/e/e3q8)

Sol: 修改

SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or 'sqlite:///' + os.path.join(basedir,'data.sqlite')
---> 去掉TEST 把生产环境和测试环境的数据库设置成一样的了

Pro: 修改后继续部署 报错:

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) relation "users" does not exist
[SQL: 'ALTER TABLE users ADD COLUMN email VARCHAR(64)'] (Background on this error at: http://sqlalche.me/e/f405)

Sol:

数据库迁移是一步步来的 最开始的时候 猜测是在最开始不懂迁移的时候把数据库迁移文件给误删了 现在我的第一个迁移文件不是创建users表 所以后面的迁移都无法进行 有两种解决方案
	1.全部删掉 重新在本地创建后上传 创建完整的模型
	2.补上删掉的users表 

先尝试第一种方案 将迁移记录全部删掉后 尝试重新生成新的文件 python manag.py db upgrade -m"the completed db" 
报错:alembic.util.exc.CommandError: Can't locate revision identified by '27474b5b9239'
	这个数字是最后一个迁移文件的编号 表名还是有记录存储在某个地方 所以猜测数据库迁移不是从前往后更新的 而是从后往前追溯定位的 最后一次的编号存储在了某个地方

现在尝试第二种方案 去作者的 github 上找到误删的那份记录 添加到本地 然后纠正一些编号 继续迁移更新
报错:alembic.util.exc.CommandError: Target database is not up to date.
把自己的原始 initial.py 这个文件删掉后一切正常  这个文件本来就是多余的 以为没什么影响就留着了 看来还是有影响

Pro: 启动服务后 一切正常 连接该目标网址时显示界面错误 查看日志 heroku logs 显示

__import__(module)
2018-06-16T05:17:56.401164+00:00 app[web.1]: ModuleNotFoundError: No module named 'flasky'

Sol:

from . import config ---> import config 修改后错误还在
查看问题记录
File "/app/.heroku/python/lib/python3.6/site-packages/gunicorn/util.py", line 350, in import_app
 __import__(module)
ModuleNotFoundError: No module named 'flasky'

猜测是不是受版本的影响,在Python3 中flasky跟Python2中不同 于是新增了一个 runtime.txt 文件夹 指定了创建的版本 
	Rs:https://blog.csdn.net/BenBoy007/article/details/79504494

这次显示创建的是Python2.7  但还是提示没有flasky 而且出现了另外一个问题
The latest version of Python 2 is python-2.7.15 (you are using python-2.7.10, which is unsupported).
	remote:We recommend upgrading by specifying the latest version (python-2.7.15).
	remote:Learn More: https://devcenter.heroku.com/articles/python-runtimes

UserWarning: The psycopg2 wheel package will be renamed from release 2.8;  Ps:改成2.8后还报错 没有这个版本 改回2.7.4 忽略此条语句

不停地修改 测试 查看日志 没解决问题 后换个关键词搜索 用 worker-failed-to-boot搜索 发现一篇文章比较契合 打开自己的Procfile 显示 web: gunicorn flasky:app 将flasky改成manage 问题解决


Sum:

Procfile 修改了app
migrations 补上了前面误删的文件
runtime.txt 指定了服务器端python的代码
git push heroku master  每次都会重新下载安装 且名字要指定
config.py 修改了ProductionConfig里的DATABASE_URI
部署到服务器时python使用最新2.7.15 不需要使用本地的Python2.7.10
创建了新的文件夹 以及git仓库
pipenv 在本地模拟服务器端的app时才会用到 和 postgresql 一样

一些网站

部署学习网站1
部署学习网站2
pipenv的介绍