Django实践:创建用户账户
引言
本文简要介绍在一个Django项目中如何让各个用户账户拥有自己的数据,包括:未登录不能访问数据,登录用户只能访问自己的数据等权限管理操作。
一、使用装饰器@login_required限制访问
1. 添加装饰器@login_required
利用装饰器(decorator)@login_required,可以让某些页面,只允许已经登录的用户访问。首先用from django.contrib.auth.decorators import login_required
导入login_required()
函数,然后@login_required放在相应的函数定义之前,在运行函数之前,会先运行装饰器login_required()
函数,检查是否已登录。以topics()
函数为例:
from django.shortcuts import render
from django.http import HttpResponseRedirect, Http404
from django.urls import reverse
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
@login_required
def topics(request):
"""显示所有的主题"""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
打开页面时候是显示主页:
点击Topics按钮的时候,显示页面找不到:
2. 重定向到登录界面
通过修改settings.py代码,将404页面重定向到登录界面,添加如下:
# My settings
LOGIN_URL = '/login/'
二、将数据管理到账户
1. 修改模型代码
首先用from django.contrib.auth.models import User
导入User
,然在Topic类中添加owner = models.ForeignKey(User, on_delete=models.CASCADE)
,将字段owner
建立起到模型User
的外键关系。
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, on_delete=models.CASCADE)
def __str__(self):
"""返回模型的字符串"""
return self.text
2. 确定当前有哪些用户
Terminal命令行中输入python manage.py shell
,然后用User.objects.all()
查看用户名和id。我们这里有两个用户mt和mat。
(base) E:\myPrograms\leraning_log>python manage.py shell
Python 3.8.3 (default, Jul 2 2020, 17:30:36) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from django.contrib.auth.models import User
In [2]: User.objects.all()
Out[2]: <QuerySet [<User: mt>, <User: mat>]>
In [3]: for user in User.objects.all():
...: print(user.username, user.id)
...:
mt 1
mat 2
3. 迁移数据库
2.3.1. makemigrations
Terminal命令行中输入python manage.py makemigrations learning_logs
,在弹出的提示中会要求我们给owner指定默认值,会让我们选择要么添加默认值,要么退出在models.py中添加默认值(黄色方框)。我们根据提示选择“1”指定默认值(红色方框),在指定默认值的时候,要求我们将用户账户id作为参数传入,我们选择“1”(绿色方框,代表mt(超级用户))将超级用户mt作为默认值指定给owner,运行成功后将field owner添加到topic中,结果如(蓝色方框)所示,成功后会在learning_logs/migrations/下增加一个0003_topic_owner.py文件。
Terminal命令行中代码如下:
(base) E:\myPrograms\leraning_log>python manage.py makemigrations learning_logs
No changes detected in app 'learning_logs'
(base) E:\myPrograms\leraning_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 t
o populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
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
Type 'exit' to exit this prompt
>>> 1
Migrations for 'learning_logs':
learning_logs\migrations\0003_topic_owner.py
- Add field owner to topic
0003_topic_owner.py中的代码如下:
rom django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('learning_logs', '0002_entry'),
]
operations = [
migrations.AddField(
model_name='topic',
name='owner',
field=models.ForeignKey(default=1, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
preserve_default=False,
),
]
2.3.2. migrate
Terminal命令行中输入python manage.py migrate
,完成数据库迁移。可以在命令行中交互的打印出topic和topic.owner验证:
(base) E:\myPrograms\leraning_log>python manage.py shell
Python 3.8.3 (default, Jul 2 2020, 17:30:36) [MSC v.1916 64 bit (AMD64)]
Type 'copyright', 'credits' or 'license' for more information
IPython 7.16.1 -- An enhanced Interactive Python. Type '?' for help.
In [1]: from learning_logs.models import Topic
In [2]: for topic in Topic.objects.all():
...: print(topic, topic.owner)
...:
Python mt
C++ mt
JAVA mt
MATLAB mt
C# mt
C mt
PostScript mt
Perl mt
三、只允许访问自己的主题
在views.py中对topics()
函数进行修改:
将代码:
topics = Topic.objects.order_by('date_added')
换成
topics = Topic.objects.filter(owner=request.user).order_by('date_added')
用filter(owner=request.user)
函数添加一个过滤器,让用户只能访问自己的主题。完整代码如下:
@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)
四、限制访问
保护用户的主题
在views.py中对topic()
函数进行修改,添加如下代码:
if topic.owner != request.user: # 验证访问页面的用户是否是拥有页面的用户
raise Http404
修改后的函数:
@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)
保护页面edit_entry
在views.py中对edit_entry()
函数进行修改,添加如下代码:
if topic.owner != request.user: # 验证访问页面的用户是否是拥有页面的用户
raise Http404
修改后的函数:
@login_required
def edit_entry(request, entry_id):
"""Edit an existing entry."""
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if topic.owner != request.user:
raise Http404
if request.method != 'POST':
# Initial request; pre-fill form with the current entry.
form = EntryForm(instance=entry)
else:
# POST data submitted; process data.
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic.id]))
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html', context)
五、将新主题关联到当前用户
在views.py中对new_topic()
函数进行修改:
将代码:
form.save()
替换成:
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
修改后的函数:
@login_required
def new_entry(request, topic_id):
"""Add a new entry for a particular topic."""
topic = Topic.objects.get(id=topic_id)
if topic.owner != request.user:
raise Http404
if request.method != 'POST':
# No data submitted; create a blank form.
form = EntryForm()
else:
# POST data submitted; process data.
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic_id]))
# context = {'topic': topic, 'form': form, 'topic_id': topic_id}
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)