开发系统和开发IDE
开发系统: Ubuntu 16.0.4 LTS
开发IDE: Visual Studio Code 版本: 1.32.3
Python版本: Python3
依赖: Django 2.2
资料《Python编程从入门到实践》书籍
GitHub:
1. Django入门
Django是一个Web框架 ——一套用于帮助开发交互式网站的工具。
学习如何使用Django来开发一个名为“学习笔记”(Learning Log) 的项目, 这是一个在线日志系统, 让你能够记录所学习的有关特定主题的知识。
1.1 建立项目
不包括虚拟环境ll_env文件夹下文件
1.1.1 建立虚拟环境
虚拟环境 是系统的一个位置, 你可以在其中安装包, 并将其与其他Python包隔离,将项目的库与其他项目分离是有益的。
为项目新建一个目录, 将其命名为learning_log,使用如下命令来创建虚拟环境:
python -m venv ll_env
运行了模块venv , 并使用它来创建一个名为ll_env的虚拟环境,成功创建如下图
1.1.2 激活虚拟环境
建立虚拟环境后, 需要使用下面的命令激活它:
source ll_env/bin/activate
成功激活如下图:
1.1.3 安装Django
在虚拟环境处于活动状态时安装Django(以下无特殊说明,均在虚拟环境处于活动状态)
pip install Django
1.1.4 在Django中创建项目
django-admin.py startproject learning_log .
注意:千万别忘了这个句点(英文输入法), 否则部署应用程序时将遭遇一些配置问题。 如果忘记了这个句点, 就将创建的文件和文件夹删除(ll_env除外) , 再重新运行这个命令。
# | 文件名 | 说明 |
---|---|---|
settings.py | 指定Django如何与你的系统交互以及如何管理项目,修改其中一些设置, 并添加一些设置 | |
urls.py | 告诉Django应创建哪些网页来响应浏览器请求 | |
wsgi.py | 帮助Django提供它创建的文件, 这个文件名是web server gateway interface(Web服务器网关接口 ) 的首字母缩写。 |
1.1.5 创建数据库
python manage.py migrate
1.1.6 查看项目
核实Django是否正确地创建了项目。 为此, 可执行命令runserver
python manage.py runserver
打开一款Web浏览器, 并输入URL: http://localhost:8000/; 如果这不管用, 请输入http://127.0.0.1:8000/。
如果出现错误消息“That port is already in use”(指定端口已被占用) , 请执行命令python manage.py runserver 8001 , 让Diango使用另一个端口; 如果这个端口也不可用, 请不断执行上述命令, 并逐渐增大其中的端口号, 直到找到可用的端口。
1.2 创建应用程序
不包括虚拟环境ll_env文件夹下文件
Django项目 由一系列应用程序组成, 它们协同工作, 让项目成为一个整体。
当前, 在前面打开的终端窗口中应该还运行着runserver 。 请再打开一个终端窗口(或标签页) , 并切换到manage.py所在的目录。 激活该虚拟环境, 再执行命令startapp :
python manage.py startapp learning_logs
命令startapp appname 让Django建立创建应用程序所需的基础设施。 新增了一个文件夹learning_logs,其中最重要的文件是models.py、 admin.py和views.py。使用models.py来定义要在应用程序中管理的数据
1.2.1 定义模型
想想涉及的数据。 每位用户都需要在学习笔记中创建很多主题。 用户输入的每个条目都与特定主题相关联, 这些条目将以文本的方式显示。 还需要存储每个条目的时间戳, 以便能够告诉用户各个条目都是什么时候创建的
models.py
初始
from django.db import models
# Create your models here.
模型告诉Django如何处理应用程序中存储的数据。 在代码层面, 模型就是一个类, 包含属性和方法。
models.py
from django.db import models
# Create your models here.
# 创建了一个名为Topic 的类, 它继承了Model ——Django中一个定义了模型基本功能的类。 Topic 类只有两个属性: text 和date_added 。我们需要的属性
class Topic(models.Model):
''' 用户学习的主题'''
# https://docs.djangoproject.com/en/2.2/ref/models/fields/#charfield
text = models.CharField(max_length = 200) # 属性text是一个CharField——由字符或文本组成的数据
# https://docs.djangoproject.com/en/2.2/ref/models/fields/#datetimefield
date_added = models.DateTimeField(auto_now_add=True) # 实参auto_add_now=True 让Django将这个属性自动设置成当前日期和时间。
def __str__(self):
"""返回模型的字符串表示"""
return self.text
1.2.2 激活模型
要使用模型, 必须让Django将应用程序包含到项目中。 为此, 打开settings.py(它位于目录learning_log/learning_log中) , 你将看到一个这样的片段, 即告诉Django哪些应用程序安装在项目中:
settings.py
初始
--snip--
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
--snip--
注Django版本差异,书中显示是一个元组。将 INSTALLED_APPS 修改如下
settings.py
--snip--
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# 我的应用程序
'learning_logs',
]
--snip--
接下来, 需要让Django修改数据库, 使其能够存储与模型Topic 相关的信息。
python manage.py makemigrations learning_logs
命令makemigrations 让Django确定该如何修改数据库, 使其能够存储与定义的新模型相关联的数据。 输出表明Django创建了一个名为0001_initial.py的迁移文件, 这个文件将在数据库中为模型Topic 创建一个表。
下面来应用这种迁移, 让Django替我们修改数据库:
python manage.py migrate
每当需要修改“学习笔记”管理的数据时, 都采取如下三个步骤: 修改models.py; 对learning_logs 调用makemigrations ; migrate让Django迁移项目。
可以查看db.sqlite3如下图,models.py中Topic 的类数据
1.2.3 Django管理网站
1. 创建超级用户
为应用程序定义模型时, Django提供的管理网站(admin site) 让你能够轻松地处理模型。
Django允许你创建具备所有权限的用户——超级用户。 权限决定了用户可执行的操作。
为在Django中创建超级用户, 请执行下面的命令并按提示做:
python manage.py createsuperuser
2. 向管理网站注册模型
Django自动在管理网站中添加了一些模型, 如User 和Group , 但对于创建的模型, 必须手工进行注册。
创建应用程序learning_logs 时, Django在models.py所在的目录中创建了一个名为admin.py的文件:
admin.py
初始
from django.contrib import admin
# Register your models here.
为向管理网站注册Topic , 请输入下面的代码:
admin.py
from django.contrib import admin
# Register your models here.
from learning_logs.models import Topic # 导入我们要注册的模型Topic
admin.site.register(Topic) # 让Django通过管理网站管理我们的模型
使用超级用户账户访问管理网站: 访问http://localhost:8000/admin/ , 并输入刚创建的超级用户的用户名和密码
确认在终端窗口中运行着Django服务器,如果没有, 请激活虚拟环境, 并执行命令python manage.py runserver 。
可以在Topics中add新的Topic,创建 Chess 和 Rock Climbing
1.2.4 定义模型Entry
要记录学到的国际象棋和攀岩知识, 需要为用户可在学习笔记中添加的条目定义模型。 每个条目都与特定主题相关联, 这种关系被称为多对一关系, 即多个条目可关联到同一个主题。
在models.py添加Entry类代码:
models.py
from django.db import models
class Topic(models.Model):
--snip--
class Entry(models.Model):
"""学到的有关某个主题的具体知识"""
# 外键是一个数据库术语, 它引用了数据库中的另一条记录; 这些代码将每个条目关联到特定的主题。 每个主题创建时, 都给它分配了一个键(或ID) 。
# https://docs.djangoproject.com/en/2.2/ref/models/fields/#foreignkey
# 注 在django2.0后定义外键和一对一关系的时候需要加on_delete选项
topic = models.ForeignKey(Topic,on_delete=models.CASCADE)
'''
书中源代码:
topic = models.ForeignKey(Topic)
'''
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
# 在Entry 类中嵌套了Meta 类。 Meta 存储用于管理模型的额外信息, 在这里, 它让我们能够设置一个特殊属性, 让Django在需要时使用Entries 来表示多个条目。
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""返回模型的字符串表示"""
return self.text[:50] + "..." # 由于条目包含的文本可能很长, 我们让Django只显示text 的前50个字符
1.2.5 迁移模型Entry
python manage.py makemigrations learning_logs
python manage.py migrate
生成了一个新的迁移文件——0002_entry.py, 它告诉Django如何修改数据库, 使其能够存储与模型Entry 相关的信息。
1.2.6 向管理网站注册Entry
修改 admin.py
admin.py
from django.contrib import admin
# Register your models here.
from learning_logs.models import Topic,Entry # 导入我们要注册的模型Topic,Entry
admin.site.register(Topic) # 让Django通过管理网站管理我们的模型
admin.site.register(Entry)
返回到http://localhost/admin/ , 你将看到learning_logs下列出了Entries。
可以向对应的Topic中添加条目
1.2.7 Django shell
输入一些数据后, 就可通过交互式终端会话以编程方式查看这些数据了。 这种交互式环境称为Django shell
python manage.py shell
1.3 创建网页: 学习笔记主页
不包括虚拟环境ll_env文件夹下文件
使用Django创建网页的过程通常分三个阶段: 定义URL、 编写视图和编写模板。 urls -> views -> html 添加网页步骤。
定义URL:让Django知道如何将浏览器请求与网站URL匹配, 以确定返回哪个网页。
编写视图:每个URL都被映射到特定的视图 ——视图函数获取并处理网页所需的数据。
编写模板:生成浏览器能够理解的网页。
1.3.1 映射URL
打开项目主文件夹learning_log中的文件urls.py, 修改为
learning_log/urls.py
from django.contrib import admin
from django.urls import path
urlpatterns = [
path('admin/', admin.site.urls),# 该模块定义了可在管理网站中请求的所有URL
path('', include('learning_logs.urls', namespace='learning_logs')),
# 代码包含实参namespace , 让我们能够将learning_logs 的URL同项目中的其他URL区分开来
]
'''
Django版本更新,书上的代码需做相应修改
书中源代码:
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'', include('learning_logs.urls', namespace='learning_logs')),
]
应改为:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('learning_logs.urls', namespace='learning_logs')),
]
'''
默认的urls.py包含在文件夹learning_log中, 现在需要在文件夹learning_logs中创建另一个urls.py文件:(注意关于 Django版本更新,书上的代码需做相应修改)
learning_logs/urls.py
"""定义learning_logs的URL模式"""
from django.urls import path,re_path
from . import views
app_name='learning_logs' # 不能少 ,否则runserver的时候就会出错
# https://docs.djangoproject.com/en/2.2/ref/urls/#module-django.urls.conf
urlpatterns = [
# 主页
path('', views.index, name='index'),# Django将在文件views.py中查找函数index()
]
# urls -> views -> html 添加网页步骤
'''
Django版本更新,书上的代码需做相应修改
书中源代码:
from django.conf.urls import url
from . import views
urlpatterns = [
# Home page.
url(r'^$', views.index, name='index'),
# Show all topics.
url(r'^topics/$', views.topics, name='topics'),
# Detail page for a single topic.
url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='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/(?P<topic_id>\d+)/", views.topic, name='topic'),
]
'''
1.3.2 编写视图
视图函数接受请求中的信息, 准备好生成网页所需的数据, 再将这些数据发送给浏览器——这通常是使用定义了网页是什么样的模板实现的。
learning_logs中的文件views.py是执行命令python manage.py startapp 时自动生成的, 当前其内容如下:
views.py
初始
from django.shortcuts import render
# Create your views here.
这个文件只导入了函数render() , 它根据视图提供的数据渲染响应。 下面的代码演示了该如何为主页编写视图:
views.py
from django.shortcuts import render
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html') # 这里向函数render() 提供了两个实参: 原始请求对象以及一个可用于创建网页的模板。
1.3.3 编写模板
模板定义了网页的结构。 模板指定了网页是什么样的, 而每当网页被请求时, Django将填入相关的数据。 模板让你能够访问视图提供的任何数据。 主页视图没有提供任何数据, 因此相应的模板非常简单。
在文件夹learning_logs中新建一个文件夹, 并将其命名为templates。 在文件夹templates中, 再新建一个文件夹, 并将其命名为learning_logs。 这好像有点多余(在文件夹learning_logs中创建了文件夹templates, 又在这个文件夹中创建了文件夹learning_logs) , 但建立了Django能够明确解读的结构, 即便项目很大, 包含很多应用程序亦如此。 在最里面的文件夹learning_logs中, 新建一个文件, 并将其命名为index.html, 再在这个文件中编写如下代码:
index.html
<p>Learning Log</p>
<p>Learning Log helps you keep track of your learning, for any topic you're learning about.</p>
HTML教程:可参考runoob HTML 教程- (HTML5 标准)
请求这个项目的基础URL——http://localhost:8000/, 将看到刚才创建的网页, 而不是默认的Django网页。
1.4 创建其他网页
不包括虚拟环境ll_env文件夹下文件
将创建两个显示数据的网页, 其中一个列出所有的主题, 另一个显示特定主题的所有条目。 对于每个网页, 都将指定URL模式, 编写一个视图函数, 并编写一个模板。 但这样做之前, 先创建一个父模板, 项目中的其他模板都将继承它。
使用Django创建网页的过程通常分三个阶段: 定义URL、 编写视图和编写模板。 urls -> views -> html 添加网页步骤。
1.4.1 模板继承
创建网站时, 几乎都有一些所有网页都将包含的元素。 在这种情况下, 可编写一个包含通用元素的父模板, 并让每个网页都继承这个模板, 而不必在每个网页中重复定义这些通用元素。
1. 父模板
首先来创建一个名为base.html的模板, 并将其存储在index.html所在的目录中。 这个文件包含所有页面都有的元素; 其他的模板都继承base.html。 当前, 所有页面都包含的元素只有顶端的标题。 将在每个页面中包含这个模板, 因此将这个标题设置为到主页的链接:
learning_logs/base.html
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>
{% block content %}{% endblock content %}
注意: {% 之间没有空格
Django 模板可阅读 runoob Django 模板
2. 子模板
重新编写index.html, 使其继承base.html, 如下所示:
learning_logs/index.html
{% 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 %}
1.4.2 显示所有主题的页面
urls -> views -> html 添加网页步骤
1. URL模式
定义显示所有主题的页面的URL。 通常, 使用一个简单的URL片段来指出网页显示的信息; 将使用单词topics, 因此URL http://localhost:8000/topics/将返回显示所有主题的页面。修改learning_logs/urls.py如下
urls.py
"""定义learning_logs的URL模式"""
from django.urls import path,re_path
from . import views
app_name='learning_logs' # 不能少 ,否则runserver的时候就会出错
# https://docs.djangoproject.com/en/2.2/ref/urls/#module-django.urls.conf
urlpatterns = [
# 主页
path('', views.index, name='index'), # Django将在文件views.py中查找函数index()
# 显示所有的主题
path('topics/',views.topics,name = 'topics')
]
2. 视图
函数topics() 需要从数据库中获取一些数据, 并将其发送给模板。 需要在views.py中添加的代码如下:
views.py
from django.shortcuts import render
from .models import Topic # 导入了与所需数据相关联的模型
# Create your views here.
def index(request):
"""学习笔记的主页"""
return render(request, 'learning_logs/index.html') # 这里向函数render() 提供了两个实参: 原始请求对象以及一个可用于创建网页的模板。
def topics(request):
"""显示所有的主题"""
topics = Topic.objects.order_by('date_added') # 查询数据库——请求提供Topic 对象, 并按属性date_added 对它们进行排序
context = {'topics': topics} # 一个将要发送给模板的上下文。 上下文是一个字典, 其中的键是我们将在模板中用来访问数据的名称, 而值是我们要发送给模板的数据。
return render(request, 'learning_logs/topics.html', context)
3. 模板
显示所有主题的页面的模板接受字典context , 以便能够使用topics() 提供的数据。 创建一个文件, 将其命名为topics.html, 并存储到index.html所在的目录中。
topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>{{ topic }}</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
现在需要修改父模板, 使其包含到显示所有主题的页面的链接:
base.html
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a> -
<a href="{% url 'learning_logs:topics' %}">Topics</a>
</p>
{% block content %}{% endblock content %}
刷新浏览器中的主页, 将看到链接Topics。 单击这个链接, 将看到如下图
1.4.3 显示特定主题的页面
创建一个专注于特定主题的页面——显示该主题的名称及该主题的所有条目
urls -> views -> html 添加网页步骤
1. URL模式
显示特定主题的页面的URL模式与前面的所有URL模式都稍有不同, 因为它将使用主题的id 属性来指出请求的是哪个主题。 例如, 如果用户要查看主题Chess(其id 为1) 的详细页面, URL将为http://localhost:8000/topics/1/。 下面是与这个URL匹配的模式, 它包含在learning_logs/urls.py中:
urls.py
"""定义learning_logs的URL模式"""
from django.urls import path,re_path
from . import views
app_name='learning_logs' # 不能少 ,否则runserver的时候就会出错
# https://docs.djangoproject.com/en/2.2/ref/urls/#module-django.urls.conf
urlpatterns = [
# 主页
path('', views.index, name='index'), # Django将在文件views.py中查找函数index()
# 显示所有的主题
path('topics/',views.topics,name = 'topics')
# 特定主题的详细页面
# use a regular expression, you can use re_path(). https://stackoverflow.com/questions/47661536/django-2-0-path-error-2-0-w001-has-a-route-that-contains-p-begins-wit
re_path(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'), # ?P<topic_id> 将匹配的值存储到topic_id 中; 而表达式\d+ 与包含在两个斜杆内的任何数字都匹配, 不管这个数字为多少位。
]
2. 视图
views.py
--snip--
def topic(request, topic_id):
"""显示单个主题及其所有的条目"""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
3. 模板
这个模板需要显示主题的名称和条目的内容; 如果当前主题不包含任何条目, 还需向用户指出这一点:
topic.html
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</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 %}
4. 将显示所有主题的页面中的每个主题都设置为链接
在浏览器中查看显示特定主题的页面前, 需要修改模板topics.html, 让每个主题都链接到相应的网页
topics.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
用模板标签url 根据learning_logs中名为topic 的URL模式来生成合适的链接。 这个URL模式要求提供实参topic_id
刷新显示所有主题的页面, 再单击其中的一个主题