Python 用户账户(让用户拥有自己的数据)

Web应用程序的核心是让任何用户都能够注册账户并能够使用它,不管用户身处何方。在本章中,你将创建一些表单,让用户能够添加主题和条目,以及编辑既有的
条目。你还将学习Django如何防范对基于表单的网页发起的常见攻击,这让你无需花太多时间考虑确保应用程序安全的问题。
然后,我们将实现一个用户身份验证系统。你将创建一个注册页面,供用户创建账户,并让有些页面只能供已登录的用户访问。接下来,我们将修改一些视图函数,
使得用户只能看到自己的数据。你将学习如何确保用户数据的安全。

让用户拥有自己的数据

用户应该能够输入其专有的数据,因此我们将创建一个系统,确定各项数据所属的用户,再限制对页面的访问,让用户只能使用自己的数据。
在本节中,我们将修改模型Topic ,让每个主题都归属于特定用户。这也将影响条目,因为每个条目都属于特定的主题。我们先来限制对一些页面的访问。

使用@login_required 限制访问

Django提供了装饰器@login_required ,让你能够轻松地实现这样的目标:对于某些页面,只允许已登录的用户访问它们。装饰器 (decorator)是放在函数定义前面的指
令,Python在函数运行前,根据它来修改函数代码的行为。下面来看一个示例。

限制对topics 页面的访问

每个主题都归特定用户所有,因此应只允许已登录的用户请求topics 页面。为此,在learning_logs/views.py中添加如下代码:

--snip--
from django.core.urlresolvers import reverse
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
--snip--
@login_required
def topics(request):
"""显示所有的主题"""
--snip--

我们首先导入了函数login_required() 。我们将login_required() 作为装饰器用于视图函数topics() ——在它前面加上符号@ 和login_required ,让Python在运
行topics() 的代码前先运行login_required() 的代码。

login_required() 的代码检查用户是否已登录,仅当用户已登录时,Django才运行topics() 的代码。如果用户未登录,就重定向到登录页面。
为实现这种重定向,我们需要修改settings.py,让Django知道到哪里去查找登录页面。请在settings.py末尾添加如下代码:

"""
项目learning_log的Django设置
--snip--
# 我的设置
LOGIN_URL = '/users/login/'

现在,如果未登录的用户请求装饰器@login_required 的保护页面,Django将重定向到settings.py中的LOGIN_URL 指定的URL。
要测试这个设置,可注销并进入主页。然后,单击链接Topics,这将重定向到登录页面。接下来,使用你的账户登录,并再次单击主页中的Topics链接,你将看到topics页面。

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

Django让你能够轻松地限制对页面的访问,但你必须针对要保护哪些页面做出决定。最好先确定项目的哪些页面不需要保护,再限制对其他所有页面的访问。你可以轻松地修改
过于严格的访问限制,其风险比不限制对敏感页面的访问更低。

在项目“学习笔记”中,我们将不限制对主页、注册页面和注销页面的访问,并限制对其他所有页面的访问。

在下面的learning_logs/views.py中,对除index() 外的每个视图都应用了装饰器@login_required :

--snip--
@login_required
def topics(request):
--snip--
@login_required
def topic(request, topic_id):
--snip--
@login_required
def new_topic(request):
--snip--
@login_required
def new_entry(request, topic_id):
--snip--
@login_required
def edit_entry(request, entry_id):
--snip--

如果你在未登录的情况下尝试访问这些页面,将被重定向到登录页面。另外,你还不能单击到new_topic 等页面的链接。但如果你输入URL http://localhost:8000/new_topic/,将重
定向到登录页面。对于所有与私有用户数据相关的URL,都应限制对它们的访问。

将数据关联到用户

现在,需要将数据关联到提交它们的用户。我们只需将最高层的数据关联到用户,这样更低层的数据将自动关联到用户。例如,在项目“学习笔记”中,应用程序的最高层数据是
主题,而所有条目都与特定主题相关联。只要每个主题都归属于特定用户,我们就能确定数据库中每个条目的所有者。

下面来修改模型Topic ,在其中添加一个关联到用户的外键。这样做后,我们必须对数据库进行迁移。最后,我们必须对有些视图进行修改,使其只显示与当前登录的用户相关
联的数据。

修改模型Topic

对models.py的修改只涉及两行代码:

from django.db import models
from django.contrib.auth.models import User
class Topic(models.Model):
"""用户要学习的主题"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
owner = models.ForeignKey(User)
def __str__(self):
"""返回模型的字符串表示"""
return self.text
class Entry(models.Model):
--snip--

我们首先导入了django.contrib.auth 中的模型User ,然后在Topic 中添加了字段owner ,它建立到模型User 的外键关系。

确定当前有哪些用户

我们迁移数据库时,Django将对数据库进行修改,使其能够存储主题和用户之间的关联。为执行迁移,Django需要知道该将各个既有主题关联到哪个用户。最简单的办法是,将既
有主题都关联到同一个用户,如超级用户。为此,我们需要知道该用户的ID。

下面来查看已创建的所有用户的ID。为此,启动一个Django shell会话,并执行如下命令:

(venv)learning_log$ python manage.py shell
❶ >>> from django.contrib.auth.models import User
❷ >>> User.objects.all()
[<User: ll_admin>, <User: eric>, <User: willie>]
❸ >>> for user in User.objects.all():
... print(user.username, user.id)
...
ll_admin 1
eric 2
willie 3
>>>

在❶处,我们在shell会话中导入了模型User 。然后,我们查看到目前为止都创建了哪些用户(见❷)。输出中列出了三个用户:ll_admin、eric和willie。
在❸处,我们遍历用户列表,并打印每位用户的用户名和ID。Django询问要将既有主题关联到哪个用户时,我们将指定其中的一个ID值。

迁移数据库

知道用户ID后,就可以迁移数据库了。

❶ (venv)learning_log$ python manage.py makemigrations learning_logs
❷ You are trying to add a non-nullable field 'owner' to topic without a default;
we can't do that (the database needs something to populate existing rows).
❸ Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
❹ Select an option: 1
❺ Please enter the default value now, as valid Python
The datetime and django.utils.timezone modules are available, so you can do e.g. timezone.now()
❻ >>> 1
Migrations for 'learning_logs':
0003_topic_owner.py:
- Add field owner to topic

我们首先执行了命令makemigrations (见❶)。在❷处的输出中,Django指出我们试图给既有模型Topic 添加一个必不可少(不可为空)的字段,而该字段没有默认值。在
❸处,Django给我们提供了两种选择:要么现在提供默认值,要么退出并在models.py中添加默认值。在❹处,我们选择了第一个选项,因此Django让我们输入默认值(见❺)。

为将所有既有主题都关联到管理用户ll_admin,我输入了用户ID值1(见❻)。并非必须使用超级用户,而可使用已创建的任何用户的ID。接下来,Django使用这个值来迁移数据
库,并生成了迁移文件0003_topic_owner.py,它在模型Topic 中添加字段owner 。

现在可以执行迁移了。为此,在活动的虚拟环境中执行下面的命令:

(venv)learning_log$ python manage.py migrate
Operations to perform:
Synchronize unmigrated apps: messages, staticfiles
Apply all migrations: learning_logs, contenttypes, sessions, admin, auth
--snip--
Running migrations:
Rendering model states... DONE
❶ Applying learning_logs.0003_topic_owner... OK
(venv)learning_log$

Django应用新的迁移,结果一切顺利(见❶)。
为验证迁移符合预期,可在shell会话中像下面这样做:

❶ >>> from learning_logs.models import Topic
❷ >>> for topic in Topic.objects.all():
... print(topic, topic.owner)
...
Chess ll_admin
Rock Climbing ll_admin
>>>

我们从learning_logs.models 中导入Topic (见❶),再遍历所有的既有主题,并打印每个主题及其所属的用户(见❷)。正如你看到的,现在每个主题都属于用户
ll_admin。

注意  你可以重置数据库而不是迁移它,但如果这样做,既有的数据都将丢失。一种不错的做法是,学习如何在迁移数据库的同时确保用户数据的完整性。如果你确
实想要一个全新的数据库,可执行命令python manage.py flush ,这将重建数据库的结构。如果你这样做,就必须重新创建超级用户,且原来的所有数据都将
丢失。

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

当前,不管你以哪个用户的身份登录,都能够看到所有的主题。我们来改变这种情况,只向用户显示属于自己的主题。

在views.py中,对函数topics() 做如下修改:

--snip--
@login_required
def topics(request):
"""显示所有的主题"""
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
--snip--

用户登录后,request 对象将有一个user 属性,这个属性存储了有关该用户的信息。代码Topic.objects.filter(owner=request.user) 让Django只从数据库中获
取owner 属性为当前用户的Topic 对象。由于我们没有修改主题的显示方式,因此无需对页面topics的模板做任何修改。

要查看结果,以所有既有主题关联到的用户的身份登录,并访问topics页面,你将看到所有的主题。然后,注销并以另一个用户的身份登录,topics页面将不会列出任何主题。

保护用户的主题

我们还没有限制对显示单个主题的页面的访问,因此任何已登录的用户都可输入类似于http://localhost:8000/topics/1/的URL,来访问显示相应主题的页面。
你自己试一试就明白了。以拥有所有主题的用户的身份登录,访问特定的主题,并复制该页面的URL,或将其中的ID记录下来。然后,注销并以另一个用户的身份登录,再输入
显示前述主题的页面的URL。虽然你是以另一个用户登录的,但依然能够查看该主题中的条目。
为修复这种问题,我们在视图函数topic() 获取请求的条目前执行检查:

from django.shortcuts import render
❶ from django.http import HttpResponseRedirect, Http404
from django.core.urlresolvers import reverse
--snip--
@login_required
def topic(request, topic_id):
"""显示单个主题及其所有的条目"""
topic = Topic.objects.get(id=topic_id)
# 确认请求的主题属于当前用户
❷ if topic.owner != request.user:
raise Http404
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
--snip--

服务器上没有请求的资源时,标准的做法是返回404响应。在这里,我们导入了异常Http404 (见❶),并在用户请求它不能查看的主题时引发这个异常。收到主题请求后,我
们在渲染网页前检查该主题是否属于当前登录的用户。如果请求的主题不归当前用户所有,我们就引发Http404 异常(见❷),让Django返回一个404错误页面。
现在,如果你试图查看其他用户的主题条目,将看到Django发送的消息Page Not Found。在第20章,我们将对这个项目进行配置,让用户看到更合适的错误页面。

保护页面edit_entry

页面edit_entry 的URL为http://localhost:8000/edit_entry/entry_id / ,其中 entry_id 是一个数字。下面来保护这个页面,禁止用户通过输入类似于前面
的URL来访问其他用户的条目:

--snip--
@login_required
def edit_entry(request, entry_id):
"""编辑既有条目"""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if topic.owner != request.user:
raise Http404
if request.method != 'POST':
# 初次请求,使用当前条目的内容填充表单
--snip--

我们获取指定的条目以及与之相关联的主题,然后检查主题的所有者是否是当前登录的用户,如果不是,就引发Http404 异常。

将新主题关联到当前用户

当前,用于添加新主题的页面存在问题,因此它没有将新主题关联到特定用户。如果你尝试添加新主题,将看到错误消息IntegrityError ,指
出learning_logs_topic.user_id 不能为NULL 。Django的意思是说,创建新主题时,你必须指定其owner 字段的值。

由于我们可以通过request 对象获悉当前用户,因此存在一个修复这种问题的简单方案。请添加下面的代码,将新主题关联到当前用户:

--snip--
@login_required
def new_topic(request):
"""添加新主题"""
if request.method != 'POST':
# 没有提交的数据,创建一个空表单
form = TopicForm()
else:
# POST提交的数据,对数据进行处理
form = TopicForm(request.POST)
if form.is_valid():
❶ new_topic = form.save(commit=False)
❷ new_topic.owner = request.user
❸ new_topic.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
--snip--

我们首先调用form.save() ,并传递实参commit=False ,这是因为我们先修改新主题,再将其保存到数据库中(见❶)。接下来,将新主题的owner 属性设置为当前用户
(见❷)。最后,对刚定义的主题实例调用save() (见❸)。现在主题包含所有必不可少的数据,将被成功地保存。
现在,这个项目允许任何用户注册,而每个用户想添加多少新主题都可以。每个用户都只能访问自己的数据,无论是查看数据、输入新数据还是修改旧数据时都如此。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/37635.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

springboot整合modbus实现通讯

springboot整合modbus4j实现tcp通讯 前言 本文基于springboot和modbus4j进行简单封装&#xff0c;达到开箱即用的目的&#xff0c;目前本方案仅实现了tcp通讯。代码会放在最后&#xff0c;按照使用方法操作后就可以直接使用 介绍 在使用本方案之前&#xff0c;有必要对modb…

【论文阅读】Contrastive Clustering Learning for Multi-Behavior Recommendation

论文地址&#xff1a;Contrastive Clustering Learning for Multi-Behavior Recommendation | ACM Transactions on Information Systems 摘要 近年来&#xff0c;多行为推荐模型取得了显著成功。然而&#xff0c;许多模型未充分考虑不同行为之间的共性与差异性&#xff0c;以…

C/C++蓝桥杯算法真题打卡(Day6)

一、P8615 [蓝桥杯 2014 国 C] 拼接平方数 - 洛谷 方法一&#xff1a;算法代码&#xff08;字符串分割法&#xff09; #include<bits/stdc.h> // 包含标准库中的所有头文件&#xff0c;方便编程 using namespace std; // 使用标准命名空间&#xff0c;避免每次调用…

纯vue手写流程组件

前言 网上有很多的vue的流程组件&#xff0c;但是本人不喜欢很多冗余的代码&#xff0c;喜欢动手敲代码&#xff1b;刚开始写的时候&#xff0c;确实没法下笔&#xff0c;最后一层一层剥离&#xff0c;总算实现了&#xff1b;大家可以参考我写的代码&#xff0c;可以拿过去定制…

[特殊字符][特殊字符][特殊字符][特殊字符][特殊字符][特殊字符]壁紙 流光染墨,碎影入梦

#Cosplay #&#x1f9da;‍♀️Bangni邦尼&#x1f430;. #&#x1f4f7; 穹妹 Set.01 #后期圈小程序 琼枝低垂&#xff0c;霜花浸透夜色&#xff0c;风起时&#xff0c;微光轻拂檐角&#xff0c;洒落一地星辉。远山隐于烟岚&#xff0c;唯余一抹青黛&#xff0c;勾勒出天光水…

kafka压缩

最近有幸公司参与kafka消息压缩&#xff0c;背景是日志消息量比较大。kafka版本2.4.1 一、确认压缩算法 根据场景不同选择不同。如果是带宽敏感患者推荐高压缩比的zstd&#xff0c;如果是cpu敏感患者推荐lz4 lz4和zstd底层都使用的是lz77算法&#xff0c;具体实现逻辑不同&am…

Java EE(14)——网络原理——UDPTCP数据报的结构

前言 本文主要介绍传输层的两个知名协议——UDP&TCP&#xff08;想了解其他层协议请移步Java EE(12)——初始网络&#xff09; 一.传输层的作用 传输层主要实现端对端的数据传输&#xff0c;在传输层的数据报中会包含源端口/目的端口的信息。端口的作用就是标识主机中的…

ccfcsp2701如此编码

//如此编码 #include<iostream> using namespace std; int main(){int n,m;cin>>n>>m;int a[21],b[21],c[21];for(int i1;i<n;i){cin>>a[i];}c[0]1;for(int i1;i<n;i){c[i]c[i-1]*a[i];}b[1](m%c[1])/c[0];int s1,s20;for(int i2;i<n;i){s2s2…

麒麟操作系统安装人大金仓数据库

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 在当前数字化转型和信息安全备受重视的背景下&#xff0c;众多公司积极推进国产化改造进程。在操作系统领域&#xff0c;统信、open 欧拉、中标麒麟、银河麒麟等国产操作系统崭露头角&#xff0c;逐…

【工具变量】全国地级市地方ZF债务数据集(2014-2023年)

地方ZF债务是地方财政运作的重要组成部分&#xff0c;主要用于基础设施建设、公共服务及经济发展&#xff0c;是衡量地方财政健康状况的重要指标。近年来&#xff0c;我国地级市的地方ZF债务规模不断变化&#xff0c;涉及一般债务和专项债务等多个方面&#xff0c;对金融市场、…

vlan实验

一、实验拓扑及要求&#xff1a; 二、实验步骤-思路&#xff1a; 实验需求解读&#xff1a; 首先PC1和PC3所在接口为access接口&#xff0c;属于VLAN 2&#xff0c;那么首先需求在SW1和SW2创建VLAN2&#xff0c;并且配置对应连接PC的接口链路类型为Access并放通VLAN 2PC2/4/5…

[samba配置]宿主机访问虚拟机目录

[samba配置]宿主机访问虚拟机目录 1、安装和启动Samba服务 sudo apt update sudo apt install samba2、查看samba服务是否正在运行 sudo systemctl status smbd sudo systemctl status nmbd3、配置samba服务设置为开机启动。 sudo systemctl enable smbd nmbd4、创建一个共…

PDF文件转Markdown,基于开源项目marker

​ 首先我们来问下deepseek 为啥要选marker呢 基于深度学习&#xff0c;一看就逼格拉满。搞科研必备&#xff0c;效果应该不会太差。跟其他的阿猫阿狗工具没法比。 看下官网 https://github.com/VikParuchuri/marker ​ 一看头像是个印度佬&#xff0c;自吹——又快又好。…

【深度学习与大模型基础】第6章-对角矩阵,对称矩阵,正交矩阵

一、对角矩阵 对角矩阵&#xff08;Diagonal Matrix&#xff09;是一种特殊的方阵&#xff0c;其非对角线上的元素均为零&#xff0c;只有对角线上的元素可能非零。具体来说&#xff0c;对于一个 nn的矩阵 A[]&#xff0c;如果满足 则 AA 称为对角矩阵。对角矩阵通常表示为&am…

C语言 数据结构【动态顺序表】详解

引言 详细介绍了顺序表中各个接口的实现&#xff0c;一定要亲自动手敲一遍&#xff0c;要能想象出具体的图像 第一次敲可能不能完全靠自己敲出来&#xff08;很正常&#xff09;&#xff0c;过一段时间可以根据顺序表的原理敲第二遍 孰能生巧 一、线性表 在介绍顺序表之前先…

人脸表情识别系统分享(基于深度学习+OpenCV+PyQt5)

最近终于把毕业大论文忙完了&#xff0c;众所周知硕士大论文需要有三个工作点&#xff0c;表情识别领域的第三个工作点一般是做一个表情识别系统出来&#xff0c;如下图所示。 这里分享一下这个表情识别系统&#xff1a; 采用 深度学习OpenCVPyQt5 构建&#xff0c;主要功能包…

集成学习(下):Stacking集成方法

一、Stacking的元学习革命 1.1 概念 Stacking&#xff08;堆叠法&#xff09; 是一种集成学习技术&#xff0c;通过组合多个基学习器&#xff08;base learner&#xff09;的预测结果&#xff0c;并利用一个元模型&#xff08;meta-model&#xff09;进行二次训练&#xff0c…

tcping 命令的使用,ping IP 和端口

1. ‌Windows系统安装‌ ‌下载tcping工具‌&#xff1a;根据系统位数&#xff08;32位或64位&#xff09;下载对应的tcping.exe文件。‌安装步骤‌&#xff1a; 将下载的tcping.exe文件复制到C:\Windows\System32目录下。如果下载的是64位版本&#xff0c;需将文件名改为tcpi…

浅谈跨平台框架的演变(H5混合开发->RN->Flutter)

引言 这里分为四个阶段&#xff1a; 第一阶段 &#xff1a; 原生开发 第二阶段 &#xff1a; H5混合开发 第三阶段&#xff1a; 跨平台RN 第四阶段&#xff1a; 跨平台Flutter 正文 第一阶段&#xff1a; 原生开发 开发成本比较大 &#xff1a; 需要Android 和ios 开发两…

《TCP/IP网络编程》学习笔记 | Chapter 20:Windows 中的线程同步

《TCP/IP网络编程》学习笔记 | Chapter 20&#xff1a;Windows 中的线程同步 《TCP/IP网络编程》学习笔记 | Chapter 20&#xff1a;Windows 中的线程同步用户模式和内核模式用户模式同步内核模式同步 基于 CRITICAL_SECTION 的同步内核模式的同步方法基于互斥量对象的同步基于…