几分钟让你快速了解Django------Ⅶ

让用户拥有自己的数据

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

Ⅰ、使用@login_required限制访问

Django提供的装饰器@login_required,让你能够轻松地实现这样的目标:对于某些页面,只允许已经登录的用户访问它们,装饰器是放在函数定义前面的指令,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 topic(request):
	"""显示所有的主题"""
	# --snip--

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

# """项目learning_logd的Django设置"""
# --snip--

# 我的设置
LOGIN_URL = '/users/login/'

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

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

最好先确定项目的哪些页面不需要保护,再限制对其他所有页面的访问。在项目“学习笔记”中,我们将不再限制对主页、注册页面和注销页面的访问,并限制对其他所有页面的访问,打开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,都应限制对它们的访问

Ⅱ、将数据关联到用户

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

① 修改模型Topic

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

# from django.db import models
from django.contrib.auth.mosels 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需要知道该将各个既有的主题关联到哪个用户,最简单的方法就是将既有主题都关联到一个用户,如超级用户,为此我们需要知道该用户的ID,下面来查看已创建的所有用户的ID,启动一个Django shell 会话,并执行以下代码:

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

我们在shell会话中导入了模型User,然后我们查看到目前为止都创建了哪些用户,输出中列出了三个用户:ll_adminericwillie,最后我们遍历用户列表并打印每位用户的用户名和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 detatime 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,在代码的第2行Django指出我们试图给既有模型Topic添加一个必不可少(不可为空)的字段,而该字段没有默认值,在代码的第4行Django给我们提供了两种选择:要么现在提供默认值,要么退出并在models.py中添加默认值,在代码的第6行,我们选择了第一个选项,因此Django让我们输入默认值。为将所有既有主题都关联到管理用户ll_admin,我们在代码的第10行输入了用户ID值1,并非必须使用超级用户,而可使用已创建的任何用户的ID。

接下来,Django使用这个值来迁移数据库,并生成了迁移文件0003_topic_owner.py,它在模型Topic中添加字段owner,为此,我们在活动的虚拟环境下执行以下代码:

(venv)learning_log$ python manage.py migrate

为验证迁移是否符合预期,我们可在shell会话中执行以下代码验证:

>>> from learning_logs.models import Topic
>>> for topic in Topic.objects.all():
...		print(topic, topic.owner)
...
Chicken ll_admin # Chicken是我之前手动添加的主题
LOL ll_admin # LOL是我之前手动添加的主题

我们从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--

代码Topic.objects.filter(owner=request.user)让Django只从数据库中获取owner属性为当前用户的Topic对象,要查看结果,以所有既有主题关联到的用户的身份登录,并访问topics页面,你将看到所有的主题,然后注销并以另一个用户的身份登录,topics页面将不会列出任何主题。

Ⅳ、保护用户的主题

打开views.py,我们在视图函数topic()获取请求的条目前执行检查:

# from django.shortcuts import render
from django.http import HttpResponseRedirect, Http404
# from django.core.urlresolvers import reverse
# --snip--

# @login——required
# def topics(request,topic_id):
	# """显示单个主题及其所有的条目"""
	# topics = Topic.objects.get(id=topic_id)
	# 确认请求的主题属于当前用户
	if topic.owner != request.user:
		raise Http404
	# entries = topic.entry_set.order_by('-date_added')
	# context = {'topics': topics, 'entries':entries}
	# return render(request, 'learning_logs/topics.html',context)
# --snip--	

服务器上没有请求的资源时,标准的做法是返回404响应,收到主题请求后,我们在渲染网页前检查该主题是否属于当前登录的用户,如果请求的主题不归当前用户所有,我们就引发Http404异常,让Django返回一个404错误页面。现在,如果你试图查看其他用户的主题条目,将看到Django发送的消息Page Not Found

Ⅴ、保护页面edit_entry

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

# --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对象获悉当前用户,因此存在一个修复这种问题的简单方法,请打开views.py并添加以下代码,将新主题关联到当前用户:

# --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(reserve('learning_logs:topics'))
# context = {'form':form}
# return render(request, 'learning_logs/new_topic.html', context)
# --snip--

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

至此,我们创建了一个完整的功能齐全的项目,它运行在本地计算机上,功夫不负有心人,如果你真的是一步一步跟着我的话,现在已经具备少许项目经验啦,哈哈哈,但我们都知道代码的道路上是永无止境的,我们还是得虚心求教,脚踏实地地学习好任何知识,哪怕知识一个小小的知识点。希望你有所付出有所收获,不枉费你的所有时间。在后面的学习分享中,我们将对整个项目进行样式设计,让它成为一个更加可视化的网站,我们甚至还可以将其部署在服务器上,让任何人都可通过交互式互联网来注册并创建账户一起使用ta,今天的那个就到这里啦,感谢您的阅读嗷💞💞💞

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值