Python学习之路18-用户账户

本系列是对入门书籍《Python编程:从入门到实践》的笔记整理,属于初级内容。标题顺序采用书中标题。

本篇记录如何创建用户注册系统,如何实现用户输入自己的数据。

1. 前言

在本篇中,我们将:

  • 实现一个身份验证系统。

2. 让用户能够输入数据

2.1 添加新主题

和之前创建网页的步骤一样:定义URL,编写视图函数,编写模板。主要区别是,这里需要一个包含表单的模块forms.py

2.1.1 创建forms.py模块

用户输入信息时,需要进行验证,确保提交的信息是正确的数据类型,且不是恶意信息,如中断服务器的代码。然后再处理信息,并保存到数据库中。当然,这些工作很多都由Django自动完成。

在models.py所在的目录中新建forms.py模块。创建表单的最简单方法是继承Django的ModelForm类:

Python学习之路18-用户账户

最简单的ModelForm版本只包含一个内嵌的Meta类,它告诉Django根据哪个模型创建表单,以及在表单中包含哪些字段。第6行,我们根据Topic创建一个表单,该表单只包含字段text(第7行),并不为该字段生成标签(第8行)。

2.1.2 URL模式new_topic

当用户要添加新主题时,将切换到http://localhost:8000/new_topic/ 。在learning_logs/urls.py中添加如下代码:

Python学习之路18-用户账户

2.1.3 视图函数new_topic()

该函数需要处理两种情形:①刚进入new_topic网页,显示一个空表单;②对提交的表单数据进行处理,并将用户重定向到网页topics。修改views.py文件:

Python学习之路18-用户账户

2.1.4 GET请求和POST请求

创建Web应用程序时,将用到两种主要数据请求类型:GET请求和POST请求。从这俩英文单词可以看出,如果只从服务器读取数据页面,则使用GET请求;如果要提交用户填写的表单,通常使用POST请求。当然还有一些其他的请求类型,但这个项目中没有使用。本项目中处理表单都使用POST方法。

request.method存储了请求的类型(第7行代码)。

当不是POST请求时,我们生成一个空表单传递给模板new_topic.html,然后返回给用户;当请求是POST时,我们从request.POST这个变量中获取用户提交的数据,并暂存到form变量中。

通过is_valid()方法验证表单数据是否满足要求:用户是否填写了所有必不可少的字段(表单字段默认都是必填的),且输入的数据与字段类型是否一致。当然这些验证都是Django自动进行的。如果表单有效,在通过form的save()方法存储到数据库,然后通过reverse()函数获取页面topics的URL,并将其传递给HTTPResponseRedirect()以重定向到topics页面。如果表单无效,把这些数据重新传回给用户。

2.1.5 模板new_topic.html

Python学习之路18-用户账户

模板继承了base.html,因此其基本结构和项目中的其他页面相同。第6行中,参数action告诉服务器将提交的表单数据送到什么位置去处理,参数method让浏览器以POST请求的方式提交数据。

Django显示表单非常方便:只需要使用模板变量{{ form.as_p }},修饰符as_p让Django以段落格式渲染所有表单元素,这是一种整洁地显示表单的简单方法。

Django不自动创建提交表单的按钮,需自行创建。

2.1.6 链接到页面new_topic

在页面topics.html中添加一个到页面new_topic的链接:

Python学习之路18-用户账户

2.1.7 效果

以下是实际效果图:

Python学习之路18-用户账户

通过这个页面,随意添加几个主题,如下:

Python学习之路18-用户账户

2.2 添加新条目

和前面的步骤相似:创建条目表单,添加URL,添加视图,添加模板,链接到页面

2.2.1 创建条目表单

创建一个与模型Entry相关联的表单,但这个表单的自定义程度比TopicForm要高些,依然是在刚才创建的forms.py中添加:

Python学习之路18-用户账户

代码中定义了属性widgets。小部件(widget)是一个HTML表单元素,如单行文本框、多行文本框或下拉列表。通过设置属性widgets可以覆盖Django选择的默认小部件。通过Django的forms.Textarea定制字段“text”的输入小部件,将文本框的宽度设置为80列,而不是默认的40列。

2.2.2 添加URL模式new_entry

修改learning_logs/urls.py:

Python学习之路18-用户账户

该URL模式与形式为http://localhost:8000/new_entry/topi_id/ 的URL匹配,其中topic_id是主题的ID。

2.2.3 视图函数new_entry()

与函数new_topic()很像:

Python学习之路18-用户账户

new_entry()的定义包含形参topic_id,用于存储从URL中获得的值。

在调用save()时传递了参数commit=False(第14行),它让Django创建一个新的条目对象,但并不立刻提交数据库,而是暂时存储在变量new_entry中,待为这个新条目对象添加了属性topic之后再提交数据库。

在重定向时,reverse()函数中传递了两个参数,URL模式的名称以及列表args,args包含要包含在URL中的所有参数。

2.2.4 模板new_entry.html

类似于new_topic:

Python学习之路18-用户账户

注意第4行代码,改行代码返回到特定主题页面。

2.2.5 链接到页面new_entry

在显示特定主题的页面中添加到页面new_entry的链接,修改topic.html:

Python学习之路18-用户账户

2.2.6 效果

下图是实际效果,请随意添加一些条目:

Python学习之路18-用户账户

2.3.1 URL模式edit_entry

修改learning_logs/urls.py:

Python学习之路18-用户账户

2.3.2 视图函数edit_entry()

Python学习之路18-用户账户

首先获取要被修改的entry以及与该条目相关的主题。处理GET请求时,通过参数instance=entry创建EntryForm实例,该参数让Django创建一个表单,并使用既有条目对象中的信息填充它。处理POST请求时,还传入了data=request.POST参数,Django根据POST中的相关数据对entry进行修改。

2.3.3 模板edit_entry.html

Python学习之路18-用户账户

2.3.4 链接到页面edit_entry.html

在显示特定主题的页面中,需要给每个条目添加到页面edit_entry.html的链接,为此,修改topic.html:

Python学习之路18-用户账户

2.3.5 效果

以下是实际效果图:

Python学习之路18-用户账户

3. 创建用户账户

现在开始建立一个用户注册和身份验证系统。为此将创建一个新的应用程序,其中包含处理用户账户相关的所有功能。对Topic模型也要做稍许修改,让每个主题都归属于特定用户。

3.1 创建应用程序users

希望大家还记得如何使用startapp命令还创建APP:

Python学习之路18-用户账户

将APP添加到settings.py中

Python学习之路18-用户账户

在APP根目录下创建urls.py文件,并添加命名空间:

Python学习之路18-用户账户

为APP定义URL,修改项目根目录中的urls.py:

Python学习之路18-用户账户

3.2 登陆页面

使用Django提供的默认登陆视图,URL模式会有所不同。在users中的urls.py中添加如下代码:

Python学习之路18-用户账户

代码中,我们使用Django自带的login视图函数(注意,参数是login,而不是views.login)。从之前的例子可以看出,我们渲染模板的代码都是在自己写的视图函数中。但这里使用了自带的视图函数,无法自行编写进行渲染的代码。所以,我们还传了一个字典给path,告诉Django到哪里查找我们要用到的模板。注意,该模板在users中,而不是在learning_logs中。

3.2.1 新建模板login.html

在learning_log/users/templates/users中创建login.html:

Python学习之路18-用户账户

如果表单的errors属性被设置,则显示一条提示账号密码错误的信息。

3.2.2 链接到登陆页面

在base.html中添加到登陆页面的链接,让所有页面都包含它。将这个链接嵌套在一个 if 标签中,用户已登录时隐藏掉该链接:

Python学习之路18-用户账户

在Django身份验证系统中,每个模板都可使用变量user,这个变量有一个is_authenticated属性:如果用户已登录,该属性将为True,否则为False。

3.2.3 使用登陆页面

首先访问localhost:8000/admin注销超级用户,再访问localhost:8000/users/login/,得到如下页面:

Python学习之路18-用户账户

3.3 注销

并不为注销创建单独的页面,而是让用户单击一个连接用于注销并返回主页。因此,需要做如下工作:注销URL模式,新建视图,链接到注销视图

在users/urls.py中添加与http://localhost:8000/users/logout/ 匹配的URL模式:

Python学习之路18-用户账户

编写视图函数logout_view(),其中,直接调用Django自带的logout()函数,该函数要求request作为参数:

Python学习之路18-用户账户

在base.html中添加注销链接:

Python学习之路18-用户账户

3.4 注册页面

使用Django提供的表单UserCreationFrom,但编写自己的视图函数和模板。URL->view->template->link。

首先,创建注册页面的URL模式,修改users/urls.py:

Python学习之路18-用户账户

其次,创建视图register()

Python学习之路18-用户账户

以上代码在用户成功创建了用户后会自动登陆,该功能由login()函数实现。该函数将会为通过了身份验证的用户对象创建会话(session)。最后上述代码重定向到主页。

然后,编写注册页面的模板register.html

Python学习之路18-用户账户

最后,在页面中显示注册链接,修改base.html,在用户没有登录时显示注册链接:

Python学习之路18-用户账户

下面是实际效果:

Python学习之路18-用户账户

这是直接点register按钮时的反馈,不过这里有点疑惑,从上面的register.html中看到,其实代码很简单,但这里有个浮动效果,而且在注册模板中并没有像前面那样的{% form.errors %}模板标签,但依然有未注册成功时的反应,而且注册的视图函数也是自己写的,并不是用的自带的注册函数,所以不知道是不是和form.as_p有关。之后再慢慢研究吧,

4. 让用户拥有自己的数据

用户应该能够输入其专有的数据,所以应该创建一个系统,确定各项数据所属的用户,再限制对页面的访问,使得用户只能使用自己的数据,即访问控制。

4.1 使用@login_required限制访问

Django提供了装饰器@login_required,使得能轻松实现用户只能访问自己能访问的页面。

限制对topics.html的访问

每个主题都归特定用户所有,所以需要加限制,修改learning_logs/views.py:

Python学习之路18-用户账户

装饰器也是一个函数,python在运行topics()前会先运行login_required()的代码。

login_required()函数检查用户是否登录,仅当用户已登录时,Django才运行topics()函数,若未登录,就重定向到登陆界面。而为了实现这个重定向,还需要修改项目的settings.py文件,在该文件中添加这样一个常量(其实也是变量),一般在文件末尾添加:

Python学习之路18-用户账户

全面限制对项目“学习笔记”的访问

Django能轻松地限制对页面的访问,但得自己设计需要限制哪些页面。一般先确定哪些页面不需要保护,再限制对其他页面的访问。在该项目中,我们不限制对主页、注册页面和注销链接的访问,其他页面均限制。在learning_logs/views.py中,除了index()外,每个视图函数都加上@login_required。

4.2 将数据关联到用户

为了禁止用户访问其他用户的数据,需要将用户与数据关联。只需要将最高层的数据关联到用户,这样更低层的数据将自动关联到用户。下面修改Topic模型和相关视图:

Python学习之路18-用户账户

修改模型后,还需要迁移数据库。此时,需要将主题与用户关联。这里并没有通过代码进行关联,我们在迁移数据库时手动进行关联。为此,我们需要先知道有哪些用户,以及这些用户的ID。我们通过Django shell查询用户信息,当然也可以直接查看数据库,这里不再演示。我们将主题都关联到超级用户ll_admin上,它的ID是1。现在我们执行数据迁移:

Python学习之路18-用户账户

Django指出试图给既有模型Topic添加一个必不可少(不可为空)的字段,而该字段没有默认值,需要我们采取措施:要么现在提供默认值,要么退出并在models.py中添加默认值。我们选择了直接输入默认值。接下来,Django使用这个值来迁移数据库,并生成了迁移文件0003_topic_owner.py,它在模型Topic中添加字段owner。

现在执行迁移命令:

Python学习之路18-用户账户

执行后可以在Django shell中验证是否迁移成功,这里不再验证。

4.3 只允许用户访问自己的主题

目前不管以哪个用户身份登录,都能看到所有主题。现在我们添加限制,让用户只能看到自己的主题。在views.py中,对topics()做如下修改:

Python学习之路18-用户账户

用户登录后,request对象将有一个user属性,这个属性存储了有关该用户的信息。第5行代码让Django只从数据库中读取特定用户的数据。

4.4 保护用户的主题

上述代码做到了登录后只显示相应用户的数据,但是,如果登录后直接通过URL访问,如直接输入http://localhost:8000/topics/1/ ,依然可以访问不属于自己的特定主题页面。下面修改views.py中的topic()函数来加以限制:

Python学习之路18-用户账户

4.5 保护页面edit_entry

此时用户也可以像上面一样,登陆后直接通过URL来访问edit_entry.html,现在我们对这个页面也加以限制:

Python学习之路18-用户账户

4.6 最后一步:将新主题关联到当前用户

当前用于添加新主题的页面存在问题,因为它没有将新主题关联到特定用户。如果此时尝试添加新主题,将看到错误信息IntegrityError,指出learning_logs_topic.user_id不能为NULL,下面修改new_topic()函数:

Python学习之路18-用户账户

现在,这个项目允许任何用户注册,而每个用户想添加多少新主题都可以,每个用户只能访问自己的数据,无论是查看数据、输入新数据还是修改旧数据时都是如此。

5. 小结


分享到:


相關文章: