Python编程-数据可视化:第19章-用户账户
让用户输入数据
添加新主题
- 用于添加主题的表单
让用户输入并提交信息的页面都是表单,
- 创建forms 类。
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
- URL模式new_topic
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# 主页
path('', views.index, name='index'),
path('topics/', views.topics, name ='topics'),
path('topics/<int:topic_id>/', views.topic, name='topic'),
path('new_topic/', views.new_topic, name='new_topic'),
]
- 视图函数new_topic()
from django.shortcuts import render, redirect
from .models import Topic
from .forms import TopicForm
--snip--
def new_topic(request):
"""添加新主题。"""
❶ if request.method != 'POST':
# 未提交数据:创建一个新表单。
❷ form = TopicForm()
else:
# POST提交的数据:对数据进行处理。
❸ form = TopicForm(data=request.POST)
❹ if form.is_valid():
❺ form.save()
❻ return redirect('learning_logs:topics')
# 显示空表单或指出表单数据无效。
❼ context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
- 模板new_topic
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Add a new topic:</p>
<form action="{% url 'learning_logs:new_topic' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Add topic</button>
</form>
{% endblock content %}
- 链接到页面
new_topic
函数new_topic() 需要处理两种情形。一是刚进入new_topic 页面(在这种情况下应显示空表单);二是对提交的表单数据进行处理,并将用户重定向到页面topics :
添加新条目
from django import forms
from .models import Topic, Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ' '}
widgets = {'text': forms.Textarea(attrs={'cols': 80})}
- URL模式new_entry
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# 主页
path('', views.index, name='index'),
path('topics/', views.topics, name ='topics'),
path('topics/<int:topic_id>/', views.topic, name='topic'),
path('new_topic/', views.new_topic, name='new_topic'),
path('new_entry/<int:topic_id>', views.new_entry, name='new_entry'),
]
- 视图函数new_entry()
from django.shortcuts import render, redirect
from .models import Topic
from .forms import TopicForm, EntryForm
--snip--
def new_entry(request, topic_id):
"""在特定主题中添加新条目。"""
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# 未提交数据:创建一个空表单。
form = EntryForm()
else:
# POST提交的数据:对数据进行处理。
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('learning_logs:topic', topic_id=topic_id)
# 显示空表单或指出表单数据无效。
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
- 模板new_entry
{% extends "learning_logs/base.html" %}
{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}
</a></p>
<p>Add a new entry:</p>
<form action="{% url 'learning_logs:new_entry' topic.id %}"
method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name='submit'>Add entry</button>
</form>
{% endblock content %}
- 链接到页面
new_entry
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</p>
<p>
<a href="{% url 'learning_logs:new_entry' topic.id %}">Add new
entry</a>
</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
编辑条目
- URL模式edit_entry
urls.py
from . import views
app_name = 'learning_logs'
urlpatterns = [
# 主页
path('', views.index, name='index'),
path('topics/', views.topics, name ='topics'),
path('topics/<int:topic_id>/', views.topic, name='topic'),
path('new_topic/', views.new_topic, name='new_topic'),
path('new_entry/<int:topic_id>', views.new_entry, name='new_entry'),
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
]
- 视图函数edit_entry()
页面edit_entry 收到GET请求时,edit_entry() 将返回一个表
单,让用户能够对条目进行编辑;收到POST请求(条目文本经过修
订)时,则将修改后的文本保存到数据库:
from django.shortcuts import render, redirect
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
--snip--
def edit_entry(request, entry_id):
"""编辑既有条目。"""
❶ entry = Entry.objects.get(id=entry_id)
topic = entry.topic
if request.method != 'POST':
# 初次请求:使用当前条目填充表单。
❷ form = EntryForm(instance=entry)
else:
# POST提交的数据:对数据进行处理。
❸ form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
❹ form.save()
❺ return redirect('learning_logs:topic',
topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'learning_logs/edit_entry.html',
context)
- 模板edit_entry
{% block content %}
<p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic
}}</a></p>
<p>Edit entry:</p>
<form action="{% url 'learning_logs:edit_entry' entry.id %}"
method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Save changes</button>
</form>
{% endblock content %}
- 链接到页面edit_entry
创建用户账户
应用程序创建users
(ll_env) [python@node1 learning_log]$ python manage.py startapp users
安装users APP
---snip----
INSTALLED_APPS = [
'learning_logs',
'users',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
---snip----
包含users 的URL
vi learning_log/urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('users/', include('users.urls')),
path('', include('learning_logs.urls')),
]
配置登录页面
配置users APP 页面
"""为应用程序users定义URL模式。"""
from django.urls import path, include
app_name = 'users'
urlpatterns = [
# 包含默认的身份验证URL。
path('', include('django.contrib.auth.urls')),
]
模板login.html
cd ~/learning_log
mkdir -p users/templates/registration
{% extends "learning_logs/base.html" %}
{% block content %}
{% if form.errors %}
<p>Your username and password didn't match. Please try again.
</p>
{% endif %}
<form method="post" action="{% url 'users:login' %}">
{% csrf_token %}
{{ form.as_p }}
<button name="submit">Log in</button>
<input type="hidden" name="next"
value="{% url 'learning_logs:index' %}" />
</form>
{% endblock content %}
这个模板继承了base.html,旨在确保登录页面的外观与网站的其他页面相同。请注意,一个应用程序中的模板可继承另一个应用程序中的模板。如果设置表单的errors 属性,就显示一条错误消息(见❶)
指出输入的用户名密码对与数据库中存储的任何用户名密码对都不匹配
链接到登录页面
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a> -
<a href="{% url 'learning_logs:topics' %}">Topics</a> -
{% if user.is_authenticated %}
Hello, {{ user.username }}.
{% else %}
<a href="{% url 'users:login' %}">Log in</a>
{% endif %}
</p>
{% block content %}{% endblock content %}



注销
向 base.html 添加注销链接。
[python@node1 learning_log]$ vi learning_logs/templates/learning_logs/base.html
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
<a href="{% url 'learning_logs:topics' %}">Topics</a>
{% if user.is_authenticated %}
Hello, {{ user.username }}.
<a href="{% url 'users:logout' %}">Log out</a>
{% else %}
<a href="{% url 'users:login' %}">Log in</a>
{% endif %}
</p>
{% block content %}{% endblock content %}
注销时确认页面
[python@node1 registration]$ vi logged_out.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>You have been logged out. Thank you for visiting!</p>
{% endblock content %}
注销后,确认页面提示用户已成功注销。

用户注册页面
注册页面URL 模式
from django.urls import path, include
from . import views
app_name = 'users'
urlpatterns = [
# 包含默认的身份验证URL。
path('', include('django.contrib.auth.urls')),
path('register/', views.register, name='register'),
]
视图函数register()
from django.shortcuts import render
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm
# Create your views here.
def register(request):
"""注册新用户。"""
if request.method != 'POST':
# 显示空的注册表单。
form = UserCreationForm()
else:
# 处理填写好的表单。
form = UserCreationForm(data=request.POST)
if form.is_valid():
new_user = form.save()
# 让用户自动登录,再重定向到主页。
login(request, new_user)
return redirect('learning_logs:index')
# 显示空表单或指出表单无效。
context = {'form': form}
return render(request, 'registration/register.html', context)
让用户拥有自己的数据
使用@login_required 限制访问
- 限制访问显示所有主题的页面。
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
--snip--
@login_required
def topics(request):
"""显示所有的主题。"""
--snip--
login_required() 的代码检查用户是否已登录,仅当用户已登录时,Django才运行topics() 的代码。如果用户未登录,就重定向到登录页面。
为实现这种重定向,需要修改settings.py,让Django知道到哪里去查找登录页面。请在settings.py末尾添加如下代码:
--snip--
# 我的设置
LOGIN_URL = 'users:login'
- 全面限制对项目“学习笔记”的访问
Django让你能够轻松地限制对页面的访问,但你必须确定要保护哪些页面。最好先确定项目的哪些页面不需要保护,再限制对其他所有页面的访问。你可轻松地修改过于严格的访问限制。比起不限制对敏感页面的访问,这样做的风险更低。
在项目“学习笔记”中,将不限制对主页和注册页面的访问,并限制对其他所有页面的访问。
views.py
--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--
将数据关联到用户
- 修改模型Topic
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
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.CASCAE)
def __str__(self):
"""返回模型的字符串表示。"""
return self.text
class Entry(models.Model):
"""学到的有关某个主题的具体只是。"""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示。"""
return f"{self.text[:50]}..."
- 确定当前有哪些用户
(ll_env)learning_log$ python manage.py shell
❶ >>> from django.contrib.auth.models import User
❷ >>> User.objects.all()
<QuerySet [<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
>>>