Flask Web 开发 用户资料_3

继续上一章节,上一章节讲的是用户编辑自己页面的内容,内容也是相对简单,真实姓名,来自哪里,自我介绍的信息,而email和用户名什么的,你自己也没地方改,因为在注册的时候就登记了。

但是作为网站管理者,你肯定需要拥有权限可以修改用户的资料,甚至说是为他们修改email的地址,或者是用户名,还有权限

别看只是多做一个修改他人用户信息的表单,这个章节里面有几个知识点需要特别注意

进入正题,既然可以修改的内容多了,那么我们肯定要新建一个表单,作为ADMIN可以看到并修改的表单

class EditProfileAdminForm(Form):
	email = StringField('Email',validators=[Required(),Length(1,64),Email()])
	username = StringField('Username',validators=[
		Required(),Length(1,64),Regexp('^[A-Za-z][A-Za-z0-9_.]*$',0,
						'numbers,dots or underscores')])
	confirmed = BooleanField('Confirmed')
	role = SelectField('Role',coerce = int)      #注意,这里的Field是Select,也就是下拉选择列表
	name = StringField('Real Name',validators=[Length(0,64)])
	location = StringField('Location',validators=[Length(0,64)])
	about_me = TextAreaField('About me')
	submit = SubmitField('Submit')

	def __init__(self,user,*args,**kwargs):      #这里一定要注意到,在生成表单的时候,是需要带着参数user的!!!
		super(EditProfileAdminForm,self).__init__(*args,**kwargs)
		self.role.choices = [(role.id,role.name)
							for role in Role.query.order_by(Role.name).all()]
		self.user = user
	#上面的role.choices属性,是针对的表单role的选项,后面单独写
	def validate_email(self,field):
		if field.data != self.user.email and 				User.query.filter_by(email = field.data).first():   #检验email是否发生更改且是否重复
			raise ValidationError('Email already registered.')

	def validate_username(self,field):                                          #检验username是否发生更改且是否重复
		if field.data != self.user.username and 				User.query.filter_by(username = field.data).first():
			raise ValidationError('Username already in use')

先写简单的:

1:关于validate_email和validate_username,前面的章节里面我们已经提到过了,在表单的设置里面,如果设定的函数是以validate_开头的,那么,他会在验证的时候,和上面表单的普通验证一起使用。   那他是如何一一对应的呢?其实就是  validate_email ,下划线后面的内容,对应上面表单的名字,再具体一些,对应的是form.email,也就是Form实例对象的email属性。

所以field.data这句意思实际上是form.email.data != self.user.username 。

这句话什么意思呢?就是当你作为管理者在修改用户资料的时候,如果资料发生了变化,且这个变化后的email 在数据库中搜索出来已经被人使用了

则会报错:Email already registered.

同样适用于修改用户username的时候

因为,对于所有用户来说,username和email,都应该是唯一的。

----------------------------------------------------------------------这里插播Flask 源码----------------------------------------------------------------------------------

app\main\forms.py内部

from flask.ext.wtf import Form

flask.wtf\__init__.py通过form.py内导入Form

from .form import Form

而form.py内的Form定义

class Form(SecureForm):

而SecureForm则是通过下面这行导入的

from wtforms.ext.csrf.form import SecureForm

之后在wtforms\ext\csrf\form.py可以看到

class SecureForm(Form)

好,我们看到最终还是从wtforms的form里面导入的Form

from wtforms.form import Form

所以,最后我们在Form类里面找到了这个validate_的定义

----------------------------------------------------------------------------源码寻找结束-------------------------------------------------------------------------------

2:我们来将关于SelectFieldchoices的内容

class EditProfileAdminForm(Form):

#..

role = SelectField('Role',coerce = int)

#..

	def __init__(self,user,*args,**kwargs):
		super(EditProfileAdminForm,self).__init__(*args,**kwargs)
		self.role.choices = [(role.id,role.name)
							for role in Role.query.order_by(Role.name).all()]

从官方文档我们可以看到,你有2种选择方式,一种是固态的设置选项,比如下面文档截图里面的choices,写在SelectField内部

第二种是动态选项,是在额外的函数内定义,通过语言,动态地和作为类属性的SelectField链接

回到书上的例子,他显然是使用的动态设置,在__init__初始化函数里面,进行了choices的设置

for role in Role.query.order_by(Role.name).all()  这句话,通过Role内部的名字进行排列,并全部迭代出来

在前面的章节我们已经知道,目前我们有3个拥有name属性的role,name属性分别是 ‘User‘ , ‘Moderator‘ , ‘ADMINISTER

所以,对应的(role.id,role.name)就有3个选项

# 留个位置,写value和label对应的意思

这里插一个后面完成后的效果图,就能完全明白value和label对应的意思了

我们可以看到,对应我们的例子,显示出来的标签,是role.name,也就是元祖中的第二个参数label

而他对应的内部逻辑(也就是书上说的标识符),实际上就是role.id,我们可以在下面第二张图里面看到数据库的形式

所以,你选择了不同的角色name名字,就等于赋予了不同的id

这里额外再重复一点,就是在建立models的时候,Role类里面有一个relationship,他的backref是"role",我自己理解,这个就是对应User 类创建实例时,User能够通过role属性,来访问Role类,并将Role类的类对象,作为本身User的一个属性来起作用,说白了点,就是,将Role类的所有属性打包进一个实例,赋值给User的role属性。

所以,在书本上的例子里,你选择了不同的role,就等于选择了不同的用户权限等级。

表单部分写完了,就要写视图部分函数

@main.route('/edit-profile/<int:id>',methods = ['GET','POST'])
@login_required
@admin_required
def edit_profile_admin(id):                              #注意:这里的函数带参数id的
	user = User.query.get_or_404(id)
	form = EditProfileAdminForm(user = user)
	if form.validate_on_submit():
		user.email = form.email.data
		user.username = form.username.data
		user.confirmed = form.confirmed.data
		user.role = Role.query.get(form.role.data)
		user.name = form.name.data
		user.location = form.location.data
		user.about_me = form.about_me.data
		db.session.add(user)
		db.session.commit()
		flash('The profile has been updated.')
		return redirect(url_for('.user',username = user.username))
	form.email.data = user.email
	form.username.data = user.username
	form.confirmed.data = user.confirmed
	form.role.data = user.role_id      #注意:这里设置初始值的时候,用的是role_id,而role_id是外键,对应roles.id,就是roles表的id
	form.name.data = user.name
	form.location.data = user.location
	form.about_me.data = user.about_me
	return render_template('edit_profile.html',form = form,user = user)

上面这里,form.role.data预设值的这一环节,使用的是role_id来进行设置,role_id对应的是该用户,在roles表中对应的id,也就是权限等级。

class User(UserMixin,db.Model):

#...
<span style="white-space:pre">	</span>role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))    #这里的role_id,等于是roles表里的id

我们还需要再探讨一下用于选择用户角色的SelectField。设定这个字段的初始值时,role_id 被赋值给了field.role.data,这么做的原因在于choices 属性中设置的元组列表

使用数字标识符表示各选项。表单提交后,id 从字段的data 属性中提取,并且查询时会使用提取出来的id 值加载角色对象。表单中声明SelectField 时使用coerce=int 参数,

其作用是保证这个字段的data 属性值是整数。

这里很重要的一点,就是红色提示的部分,choices的属性,虽然他显示的选项是字符串的名字,比如"User" "ADMINISTER"这样的,但是,他实际上的逻辑数据是以id来控制的.

而需要注意的一个重点是:不要把form.role.data和user.role搞混淆!!

form.role.data 他对应的其实是数字

而user.role他对应的是一个类对象!所以在提交表单时,他是通过user.role = Role.query.get(form.role.data)来提取的。

逻辑部分讲完了,就轮到渲染的页面了

作为管理员,肯定是在要看到的页面上多一个按钮,可以进行管理员模式的编辑

所以,代码如下,有判断语句,当你是管理员的时候,才会显示那个按钮

{% if current_user.is_administrator() %}

<a class="btn btn-danger"
href="{{ url_for('.edit_profile_admin', id=user.id) }}">
Edit Profile [Admin]</a>

效果图如下

在关于这个views函数如何对id取值这个问题上,纠结了整整半天

因为不知道这个id的值是如何取到的,本来去templates/user里面找,发现里面的user.id是由user生成的,但是user是通过views函数导入的,然而,user却又是通过id在数据库内取值取到的,这不是死循环了么?

这里其实自己走进了一个误区,也是自己前文没有看仔细

当前页面,其实是建立在xxxxxx/user/AllenXu上面的,而不是xxxxxx/edit-profile上面的

什么意思呢?就是比如作为管理员的你,进入了普通用户Bob的user页面,那么url肯定是xxxxxx/user/Bob

那这时候的user是谁呢?这时候当前页面的user这个变量,其实就是Bob

所以,作为管理员来说,按个红色的编辑按钮,是建立在user这个页面上的!!!所以当前的user才能取得到值。

并非是我之前以为的建立在edit页面内的!!

下面是user的views函数的定义,我们可以看到传入模板的user,其实是通过username来过滤出来的,而username就是这个页面属于哪个用户的意思

所以,在user/<username>这个页面上的所有操作,user这个变量是属于<username>这个用户的,而不是你管理员!!!

current_user是管理员

user是该页面所代表的用户



时间: 2024-10-09 02:11:54

Flask Web 开发 用户资料_3的相关文章

Flask之旅《Flask Web开发:基于Python的Web应用开发实战》学习笔记

<Flask Web开发:基于Python的Web应用开发实战> 点击上方的"目录"快速到达哦! 虽然简单的网站(Flask+Python+SAE)已经上线,但只是入门.开发大型网站,系统地学习一遍还是有必要的. 1 虚拟环境 2016-6-8 书上介绍了 virtualenv,每个venv都会拷贝一份packages到项目 /venv目录. virtualenv venv venv\Scripts\activate.bat (venv) $ pip freeze >

《Flask Web开发——基于Python的Web应用开发实践》一字一句上机实践(下)

目录 前言 第8章 用户认证 第9章 用户角色 第10章 用户资料 第11章 博客文章 第12章 关注者 第13章 用户评论 第14章 应用编程接口   前言 第1章-第7章学习实践记录请参见:<Flask Web开发——基于Python的Web应用开发实践>一字一句上机实践(上) 本文记录自己学习<Flask Web开发——基于Python的Web应用开发实践>的第8章-第14章内容.相比于刚开始学习第1-7章内容来说,本部分内容实战性更强,而且在书本上遇到的问题也相对较少,如果

《Flask Web开发:基于Python的Web应用开发实战》pdf 免费下载

<Flask Web开发:基于Python的Web应用开发实战>pdf 免费下载链接: https://u253469.ctfile.com/fs/253469-292665036 第一部分 Flask 简介第1 章 安装 .........................................................................................................................................

《Flask Web开发:基于Python的Web应用开发实战》pdf 完整版免费下载

<Flask Web开发:基于Python的Web应用开发实战>.pdf pdf 完整版免费下载: https://u253469.ctfile.com/fs/253469-292665036 更多电子书下载: http://hadoopall.com/book 内容简介 本书不仅适合初级Web开发人员学习阅读,更是Python程序员用来学习高级Web开发技术的优秀参考书. ? 学习Flask应用的基本结构,编写示例应用: ? 使用必备的组件,包括模板.数据库.Web表单和电子邮件支持: ?

Flask Web开发:基于Python的Web应用开发实战PDF

Flask Web开发:基于Python的Web应用开发实战PDF 百度网盘 链接:https://pan.baidu.com/s/1_Ax_ubMUOwwfoNPUn2mDeQ 提取码:s39f 复制这段内容后打开百度网盘手机App,操作更方便哦 内容简介  · · · · · · 本书不仅适合初级Web开发人员学习阅读,更是Python程序员用来学习高级Web开发技术的优秀参考书. ? 学习Flask应用的基本结构,编写示例应用: ? 使用必备的组件,包括模板.数据库.Web表单和电子邮件支

Flask web开发----使用mysql数据库

根据<flask web开发>书中说不建议在生产环境中使用SQLite,所以在我跟着书本敲完api这一章的代码时,准备将数据库改用成mysql 1.现将这俩个文件删除,因为这是之前使用的SQLite数据库相关的文件 2.将原项目中的config文件中数据库的地址修改为如图所示的Mysql数据库.你需要现在你本机电脑上安装Mysql数据库,而且你存在一个用户的账户为:root,密码为:root,创建的数据库名称为Flask.(这里对mysql不做过多的讲解) 出于安全考虑可以将路径通过 (ven

flask web开发笔记 -- 快速入门

flask web开发笔记 -- 快速入门 初始化 Flask应用需要创建应用实例. Web服务器通过Web Server Gateway Interface (WSGI)协议把从客户端接收到的请求传递给该对象.应用程序实例是Flask类对象,通常创建如下: from flask import Flask app = Flask(__name__) Flask类的构造函数唯一的参数是应用的主模块名或包名,用于确定应用的根目录.对于大多数应用程序,使用Python的__name__变量即可. 路由

flask web开发笔记 -- 表单 --待整理

虽然Flask的请求对象给表单处理提供了足够的支持,但也有一些任务繁琐和重复.比如为表单生成HTML代码和验证提交表单数据. Flask-WTF扩展能解决上述问题.它基于wtforms 防止跨站请求伪造 跨站请求伪造(Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法.跟跨网站脚本(XSS)相比,XSS

flask web开发笔记 -- 数据库

关系数据库基于关系,有主键.外键等.它存储数据高效,避免了重复:修改数据方便.缺点是连接的成本过高. NoSQL尽量避免连接,但是增加了数据冗余,修改数据麻烦,不过查询快速. 对于中小型的应用,两者的效率差别不大. python数据库框架 Flask对数据库没有限制,你可以使用MySQL, Postgres, SQLite, Redis, MongoDB或CouchDB等.另外还有数据抽象层SQLAlchemy和MongoEngine等. 选择数据库时需要关注: 易用性:抽象层,比如ORM(ob