《Python编程 从入门到实践》第二部分阅读笔记:Django
项目3 Web应用程序——Django
第18章 Django入门
- 以编写一个“学习笔记”的Web应用程序为例
18.1 建立项目
- 以规范的形式对项目进行描述,建立虚拟环境,创建项目
- 创建与激活虚拟环境
pip
命令安装Django,并用Django
创建项目——包含四个文件:__init__.py settings.py urls.py wsgi.py
settings.py
:指定Django
如何与系统交互以及如何管理项目urls.py
:应创建哪些网页来响应浏览器请求wsgi.py
:帮助Django
提供它创建的文件
- 为项目创建数据库
python manage.py migrate
- 首次执行命令
migrate
时,将让Django
确保数据库与项目的当前状态匹配。会表明将创建必要的数据库表
- 项目查看:
python manage.py runserver
18.2 创建应用程序
python manage.py startapp 项目名
:使Django
建立创建应用所需要的基础设置。生成文件admin.py __init__.py migrations models.py tests.py views.py
定义模型(
models.py
)模型告诉
Django
如何处理应用程序中存储的数据。模型是一个类。举例:类
Topic
继承了类Model
(一个定义了模型基本功能的类)1
2
3
4
5
6
7
8class Topic(models.Model):
"""用户学习的主题"""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""返回模型的字符串表示"""
return self.text- 属性
text
是一个由字符或文本组成的数据,类型为CharField
,必须告诉Django
需要预留的空间大小,即max_length
- 属性
date_added
是一个记录日期和时间的数据,类型为DateTimeField
,用户创建新主题时,自动将属性设置为当前日期和时间,即设定参数auto_now_add
- 函数
__str__()
定义了默认使用什么属性来显示有关的模型信息
- 属性
激活模型
将应用程序包含到项目中(通过
settings.py
)在元组
INSTALLED_APPS
中添加应用程序的名字(之前startapp
创建的项目名)命令
python manage.py makemigrations 项目名
:修改数据库,使其能存储与模型相关的信息- 再次执行命令
python manage.py migrate
迁移 - 每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:修改
models.py
;对learning_logs(项目名)
调用makemigrations
;让Django
迁移项目。
- 再次执行命令
Django
管理网站创建超级用户:
- 命令:
python manage.py createsuperuser
- 之后在命令行窗口填充超级用户的信息
- 命令:
向管理网站注册模型
Django
自动在管理网站中添加了一些模型User
和Group
,但自己创建的模型必须手工注册在
admin.py
中:1
2
3from django.contrib import admin
from learning_logs.models import Topic
admin.site.register(Topic)访问网站
http://localhost:8000/admin/
并输入超级用户的用户名和密码,可添加和修改用户和用户组,并管理先前注册的模型
添加主题:
- 在管理网站的模型中创建新的主题(单击Add,填写对应表单即可)
定义模型
Entry
在本例中,需要定义模型——用户在学习笔记中添加的条目。其中,多个条目可以关联到一个主题
模型同样继承了基类
Model
代码:属性
topic
为外键,引用了数据库的另一条记录。类Meta
用于管理模型的额外信息1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16from django.db import models
class Topic(models.Model):
--snip--
class Entry(models.Model):
"""学到的有关某个主题的具体知识"""
topic = models.ForeignKey(Topic)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示"""
return self.text[:50] + "..."
迁移模型
Entry
:和之前迁移类似管理网站注册
Entry
:注册过程类似Django Shell
通过交互式终端会话查看数据。交互式环境称
Django shell
代码:
1
2
3
4(ll_env)learning_log$ python manage.py shell
from learning_logs.models import Topic
all() # 获取模型Topic的所有实例,返回一个称为查询集的列表。前者为topic.id,后者为topic Topic.objects.
[<Topic: Chess>, <Topic: Rock Climbing>]获取对象:
1
t = Topic.objects.get(id=1)
查看与主题相关联的条目(利用外键)
1
t.entry_set.all()
18.3 创建网页:学习笔记主页
网页创建阶段:定义URL,编写视图,编写模板
- 视图函数获取并处理网页需要的数据
- 视图函数通常调用一个模板,以生成浏览器理解的网页
映射URL
代码:项目主文件夹
learning_log
中的urls.py
1
2
3
4
5
6from django.conf.urls import include, url # 导入帮助项目和管理网站管理URL的函数和模块
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)), # 管理网站中请求的所有URL
url(r'', include('learning_logs.urls', namespace='learning_logs')),
] # 实参 namespace 将项目的URL同其他URL区分开代码:项目文件夹(通过
startapp
创建)learning_logs
中的urls.py
1
2
3
4
5
6
7"""定义learning_logs的URL模式"""
from django.conf.urls import url # 用于将URL映射到视图
from . import views
urlpatterns = [
# 主页
url(r'^$', views.index, name='index'),
] # 包含能够在应用程序learning_logs中请求的网页。参数1是正则表达式,定义Django可查找的模式,如果请求的URL不与任何URL模式匹配,则返回一个错误页面;参数2指定了要调用的视图函数;参数3将这个URL模式的名称定义为Index
编写视图
learning_logs
中的文件views.py
是执行命令startapp
时自动生成的代码:
1
2
3
4from django.shortcuts import render
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html') # 根据视图提供的数据渲染当URL请求与先前定义的模式匹配时,
Django
在文件views.py
中查找函数index()
,再将请求对象传递给这个视图函数。此函数只包含调用render()
的代码。实参为:原始请求对象以及可用于创建网页的模板
编写模板
模板定义网页结构。网页被请求时,
Django
将填入相关数据在
learning_logs
新建文件夹templates
,在templates
建立文件夹learning_logs
并新建文件index.html
代码:
1
2
3<!--标签<p>标识段落,指明开头位置/结尾位置。第一个段落为标题,第二个也是标题-->
<p>Learning Log</p>
<p>Learning Log helps you keep track of your learning, for any topic you're learning about.</p>
18.4 创建其他网页
模板继承:有一些所有网页都包含的元素——需要一个包含通用元素的父模板,让所有网页都继承它
父模板:
创建
base.html
并放在模板所在的目录将这个标题设置为到主页的链接
代码:
1
2
3
4<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>
{% block content %}{% endblock content %} <!--模板标签,是一小段代码,生成要在网页显示的信息-->
子模板:
重新编写
index.html
,使其继承base.html
代码:
1
2
3
4
5{% extends "learning_logs/base.html" %} <!--表明继承的父模板-->
{% block content %} <!--所有不是从父模板继承的内容都包含到这里-->
<p>Learning Log helps you keep track of your learning, for any topic you're
learning about.</p>
{% endblock content %}
显示所有主题的页面
URL模式:定义显示所有主题的页面的URL
在
learning_logs/urls.py
中修改代码:
1
2
3
4
5
6
7
8"""为learning_logs定义URL模式"""
--snip--
urlpatterns = [
# 主页
url(r'^$', views.index, name='index'),
# 显示所有的主题
url(r'^topics/$', views.topics, name='topics'),
]
视图:函数
topics()
需要从数据库中获得数据并发送给模板代码:
1
2
3
4
5def topics(request):
"""显示所有的主题"""
topics = Topic.objects.order_by('date_added') # 查询数据库,请求提供Topic对象,并按属性排序,查询集存储在topics中
context = {'topics': topics} # 定义一个发送给模板的上下文
return render(request, 'learning_logs/topics.html', context)
模板
显示特定主题的页面
- URL模式
- 视图
- 模板
第19章 用户账户
19.1 让用户能输入数据
- 添加新主题
- 添加新条目
- 编辑条目
19.2 创建用户账户
建立一个用户注册和身份验证系统
通过
startapp
建立应用程序users
将新的应用程序添加到
settings.py
中的INSTALLED_APPS
修改根目录中的
urls.py
,使其包含应用程序定义的URL(创建命名空间users
)设置登录页面
新建
learning_log/users/urls.py
1
2
3
4
5
6
7from django.conf.urls import url
from django.contrib.auth.views import login
from . import views
urlpatterns = [
# 登录页面
url(r'^login/$', login, {'template_name': 'users/login.html'}, name='login'),
]新建
learning_log/users/templates/users/login.html
1
2
3
4
5
6
7
8
9
10
11
12{% extends "learning_logs/base.html" %}
{% block content %}
{% if form.errors %} <!-- 表单的errors属性被设置,我们就显示一条错误消息 -->
<p>Your username and password didn't match. Please try again.</p>
{% endif %}
<form method="post" action="{% url 'users:login' %}"> <!--要让登录视图处理表单,因此将实参action设置为登录页面的URL-->
{% csrf_token %}
{{ form.as_p }} <!--显示表单-->
<button name="submit">log in</button> <!--提供按钮-->
<input type="hidden" name="next" value="{% url 'learning_logs:index' %}" /> <!--隐藏的表单元素next,实参value告知Django在用户登录后重定向的位置-->
</form>
{% endblock content %}base.html
中添加到登录界面的链接,让所有页面都包含。而用户登录后,则不显示这个链接,因此嵌套在一个{%if%}
的标签中1
2
3
4
5
6
7
8
9
10<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 %}
注销
在
users/urls.py
设置注销URL设置视图函数
logout_view()
,导入Django
的函数logout()
并调用,重定向到主页views.py
1
2
3
4
5
6
7from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth import logout
def logout_view(request):
"""注销用户"""
logout(request)
return HttpResponseRedirect(reverse('learning_logs:index'))在
base.html
添加注销链接,让页面都包含它。放入{% if user.is_authenticated %}
,用户仅在登录后才能看到1
2
3
4
5
6
7
8--snip—
{% 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 %}
--snip--
注册
users/urls.py
添加注册页面的URL设置视图函数
register()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.forms import UserCreationForm
def logout_view(request):
--snip--
def register(request):
"""注册新用户"""
if request.method != 'POST':
# 显示空的注册表单
form = UserCreationForm()
else:
# 处理填写好的表单
form = UserCreationForm(data=request.POST)
if form.is_valid():
new_user = form.save()
# 让用户自动登录,再重定向到主页
authenticated_user = authenticate(username=new_user.username,
password=request.POST['password1'])
login(request, authenticated_user)
return HttpResponseRedirect(reverse('learning_logs:index'))
context = {'form': form}
return render(request, 'users/register.html', context)设置注册模板
register.html
base.html
中链接到注册页面
19.3 限制用户的访问
创建一个系统,确定各项数据所属的用户,再限制对页面的访问,让用户只能使用自己的数据
限制访问
装饰器
login_required
1
2
3
4
5
6
7from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
--snip--
# 只允许已登录的用户请求topics页面,Python在运行topics()的代码前先运行login_required()的代码
def topics(request):
"""显示所有的主题"""修改
settings.py
,以使用户未登录时能重定向到登录页面1
LOGIN_URL = '/users/login/'
其他页面的登录访问控制同上
数据关联到用户
- 为数据的
model
添加外键,并迁移数据库
- 为数据的
只允许用户访问自己的主题
修改
views.py
1
2
3
4
5
6
def topics(request):
"""显示所有的主题"""
topics = Topic.objects.filter(owner=request.user).order_by('date_added') # Django只从数据库中获取owner属性为当前用户的Topic对象
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
保护页面(禁止用户通过输入类似的URL访问其他用户的内容)
在视图函数中,增加代码
1
2if topic.owner != request.user:
raise Http404
第20章 设置应用程序样式与部署
20.1 设置项目样式
- 使用
Bootstrap
库和应用程序django-bootstrap3
赋予应用程序简单而专业的外观
20.2 部署
- 将项目部署到
Heroku
的服务器