Python编程:Django 入门

当今的网站实际上都是富应用程序(rich application), 就像成熟的桌面应用程序一样。Python提供了一组开发Web应用程序的卓越工具。使用Django开发一个名为“学习笔记”(Learning Log)的项目,这是一个在线日志系统,让你能够记录所学习的有关呢特定主题的知识。

Django是一个Web框架—一套用于帮助开发交互式网站的工具。Django能够响应网页请求,还能让你更轻松地读写数据库、管理用户等。

1. 建立项目

1.1 制定规范

我们要编写一个名为“学习笔记”的Web应用程序, 让用户能够记录感兴趣的主题、并在学习每个主题的过程中添加日志条目。“学习笔记”的主页对这个网站进行描述,并邀请用户注册或登录。用户登录后,就可以创建新主题、添加新条目以及阅读既有的条目。

1.2 建立虚拟环境

要使用Django, 首先需要建立一个虚拟工作环境。虚拟环境是系统的一个位置,你可以在其中安装包,并将其与其他Python包隔离。将项目的库与其他项目分离是有益的,为项目新建一个目录,将其命名为learning_log,再在终端切换到这个目录,并创建一个虚拟环境。

⚠️:本人的操作环境:

​ macOS Monterey Version 12.4

​ 开发环境:PyCharm2020

命令如下:

(venv) (base) liuxiaowei@localhost learning_log % python -m venv 11_env

1.3 激活虚拟环境

建立虚拟环境后,需要使用下面的命令激活它:

(venv) (base) liuxiaowei@localhost learning_log % source 11_env/bin/activate
(11_env) (base) liuxiaowei@localhost learning_log % 

这个命令11_env/bin中的脚本activate。环境处于活动状态时,环境名将包含在括号内,如下图所示:

在这种情况下,你可以在环境中安装包,并使用已安装的包。而且在11_env中安装的包仅在该环境处于活动状态时才可用。要停止使用虚拟环境,可执行命令deactivate:

(11_env) (base) liuxiaowei@localhost learning_log % deactivate

如果关闭运行虚拟环境的终端,虚拟环境也将不再处于活动状态。

1.4 安装Django

创建并激活虚拟环境后,就可以安装Django了(本案例使用1.11版):

(11_env) (base) liuxiaowei@localhost learning_log % pip install Django==1.11
Collecting Django==1.11
  Using cached Django-1.11-py2.py3-none-any.whl (6.9 MB)
Collecting pytz
  Using cached pytz-2022.1-py2.py3-none-any.whl (503 kB)
Installing collected packages: pytz, Django
Successfully installed Django-1.11 pytz-2022.1

备注:Django仅在虚拟环境处于活动状态时才可用。

1.5 在Django中创建项目

在依然处于活动的虚拟环境的情况下(11_env包含在括号内),执行如下命令来新建一个项目:

(11_env) (base) liuxiaowei@localhost learning_log % django-admin.py startproject learning_log .
(11_env) (base) liuxiaowei@localhost learning_log % ls
11_env          learning_log    manage.py
(11_env) (base) liuxiaowei@localhost learning_log % ls learning_log
__init__.py     settings.py     urls.py         wsgi.py

这个命令末尾的句点让新项目使用合适的目录结构,这样开发完成后可以轻松地将应用程序部署到服务器。目录learning_log包含4个文件,其中最重要的是settings.py、urls.py和wsgi.py。文件settings.py指定Django如何与你的系统交互以及如何管理项目。文件urls.py告诉Django应创建哪些网页来响应浏览器请求。文件wsgi.py帮助Django提供它创建 的文件,这个文件名是web server gateway interface(Web服务器网关接口)的首字母缩写。

1.6 创建数据库

Django将大部分与项目相关的信息都存储在数据库中,因此我们需要创建一个供Django使用的数据库。为给项目“学习笔记”创建数据库,请在处于活动虚拟环境中情况下执行下面的命令:

(11_env) (base) liuxiaowei@localhost learning_log % python manage.py migrate
Traceback (most recent call last):
......
SyntaxError: Generator expression must be parenthesized

注意:本人执行这个命令的时候,报错。如下图所示:

解决办法:如上图箭头所指,去掉这个逗号就可以解决了。

(11_env) (base) liuxiaowei@localhost learning_log % ls
11_env          db.sqlite3      learning_log    manage.py

我们将修改数据库称为迁移数据库。首先执行命令migrate时,将让Django确保数据库与项目的当前状态匹配。运行命令ls,其输出表明Django又创建了一个文件—db.sqlite3。SQLite时一种使用单个文件的数据库,是编写简单应用程序的理想选择。

1.7 查看项目

核实Django是否正确地创建了项目。执行命令runserver, 如下所示:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py runserver
Performing system checks...

System check identified no issues (0 silenced).
June 14, 2022 - 03:57:41
Django version 1.11, using settings 'learning_log.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Django启动一个服务器,让你能够查看系统中的项目,了解它们的工作情况。在浏览器中输入URL以请求网页时,该Django服务器将进行响应:生成合适的网页,并将其发送给浏览器。现在打开一款Web浏览器,并输入URL:http://127.0.0.1:8000/;将看到如下图所示的页面:

这个页面是Django创建的,上图表示一切正常。现在暂时不关闭这个服务器,若要关闭这个服务器,按Ctrl+C即可。

2. 创建应用程序

Django项目由一系列应用程序组成,它们协同工作,让项目成为一个整体。在前面打开的终端窗口中应该还运行着runserver。请再打开一个终端窗口,并切换到manage.py所在的目录。激活该虚拟环境,再执行命令startapp:

(venv) (base) liuxiaowei@MacBook-Air learning_log % source 11_env/bin/activate
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py startapp learning_logs
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls
11_env          db.sqlite3      learning_log    learning_logs   manage.py
(11_env) (base) liuxiaowei@MacBook-Air learning_log % ls learning_logs/
__init__.py     admin.py        apps.py         migrations      models.py       tests.py        views.py

命令startup appname让Django建立创建应用程序所需的基础设施。查看项目目录,将看到其中新增一个文件夹learning_logs,打开这个文件夹,其中最重要的文件是models.py、admin.py和views.py。models.py来定义要在应用程序中管理的数据。

2.1 定义模型

打开models.py,创建自己的模型。模型告诉Django如何处理应用程序中的存储数据。在代码层面,模型就是一个类,包含属性和方法。下面表示用户将要存储的主题的模型。代码如下:

models.py

from django.db import models

# Create your models here.
class Topic(models.Model):
    """用户学习的主题"""
    text = models.CharField(max_length=200)  # 存储少量文本,如名称、标题和城市等
    date_added = models.DateTimeField(auto_now_add=True) # 当用户创建主题时,自动设置成当前日期和时间
    
    
    def __str__(self):
        """返回模型的字符串表示"""
        return self.text

我们创建了一个Topic的类,它继承了Model–Django中一个定义了模型基本功能的类。Topic类只有两个属性:text和date_added。

2.2 激活模型

要使用模型,必须让Django将应用程序包含到项目中。为此,打开settings.py(位于目录learning_log/learning_log中),看到如下内容:

settings.py


---snip---
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
---snip---

这是一个列表,告诉Django项目是由哪些应用程序组成的。请将INSTALLED_APPS修改成如下所示:

---snip---
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
  	# My app
  	'learning_logs',
]
---snip---

这里新建一个名为My app的片段,当前它只包含应用程序learning_logs。然后在终端窗口中执行如下命令:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
  learning_logs/migrations/0001_initial.py
    - Create model Topic

命令makemigrations让Django确定该如何修改数据库,使其能够存储与我们定义的新模型相关联的数据。输出表明Django创建了一个名为0001_initial.py的迁移文件,这个文件将在数据库中为模型Topic创建一个表。下面应用这种迁移,让Django替我们修改数据库;命令如下:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
  Applying learning_logs.0001_initial... OK

备注: 每当需要修改“学习笔记”管理的数据时,都采取如下三个步骤:修改models.py;对learning_logs调用make migrations;让Django迁移项目。

2.3 Django管理网站

1. 创建超级用户

Django允许你创建具备所有权限的用户–超级用户。权限决定了用户可执行的操作。在Django中创建超级用户,执行如下命令:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py createsuperuser
Username (leave blank to use 'liuxiaowei'): lxw_admin  
Email address: 115323222@qq.com
Password: 
Password (again): 
Superuser created successfully.

2. 向管理网站注册模型

我们创建应用程序learning_logs时,Django在models.py所在目录中创建了一个名为admin.py的文件,为向管理网站注册Topic,请在admin.py中输入如下代码:

admin.py

from django.contrib import admin

from learning_logs.models import Topic

# Register your models here.

admin.site.register(Topic)

这些代码导入我们要注册的模型Topic,再使用admin.site.register()让给你Django通过管理网站管理我们的模型。现在使用超级用户账户访问管理网站:访问http://localhost:8000/admin/,并输入刚刚创建的超级用户的用户名和密码,如下图所示:

3. 添加主题

单击Topics进入主题网页,几乎是空的,因为我们没有添加任何主题,单击Add,打开一个添加新主题的表单,在第一个框内输入Chess,再单击Save,返回到主题管理页面,其中包含刚刚创建的主题。如下图所示:

本案例创建Chess和Rock Climbing两个主题。

2.4 定义模型Entry

要记录学到的国际象棋和攀岩知识,需要为用户可在学习笔记中添加的条目定义模型。每个条目都与特定主题相关联,这种关系被称为多对一关系,即多个条目可关联到同一个主题。

下面是模型Entry的代码:

models.py

from django.db import models

# Create your models here.
class Topic(models.Model):
    ---snip---

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 self.text[:50] + '...'

2.5 迁移模型Entry

由于我们添加了一个新模型因此需要再次迁移数据库。修改models.py,执行命令 python manage.py makemigrations app_name ,再执行命令python manage.py migrate。

命令如下:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
  learning_logs/migrations/0002_entry.py
    - Create model Entry
(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
  Applying learning_logs.0002_entry... OK

生成了一个新的迁移文件—0002_entry.py, 它告诉Django如何修改数据库,使其能够存储与模型Entry相关的信息。执行命令migrate,我们发现Django应用了这种迁移且一切顺利。

2.6 向管理网站注册Entry

我们还需要注册模型Entry。为此需要将admin.py修改成类似如下的内容:

admin.py

from django.contrib import admin

from learning_logs.models import Topic, Entry

# Register your models here.

admin.site.register(Topic)
admin.site.register(Entry)

返回到http://localhost:8000/admin/,你将看到learning_logs下列出了Entries。单击Entries的Add链接,或者单击En tries再选择Add entry。

2.7 Django shell

输入一些数据以后, 就可以通过交互式终端会话以编程方式查看这些数据了。这种交互式环境称为Django shell,是测试项目和排除其故障的理想之地。下面是一个交互式shell会话示例:

(11_env) (base) liuxiaowei@MacBook-Air learning_log % python manage.py shell
Python 3.9.12 (v3.9.12:b28265d7e6, Mar 23 2022, 18:17:11) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>

在活动的虚拟环境中执行时,命令python manage.py shell 启动一个python解释器,可使用它来探索存储在项目数据库中的数据。在这里,我们导入模块learning_logs.models中的模型Topic,然后使用方法Topic.objects.all()来获取模型Topic的所有实例;它返回的是一个列表,称为查询集(queryset)。

下面演示如何查看分配给每个主题对象的ID:

>>> topics = Topic.objects.all()
>>> for topic in topics:
...     print(topic.id, topic)
... 
1 Chess
2 Rock Climbing

从以上输出可知,主题Chess的ID为1,而Rock Climbing的ID为2。知道对象的ID后, 就可获取该对象并查看其任何属性。通过如下命令可以查看主题Chess的属性text和date_added的值:

>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2022, 6, 14, 9, 4, 29, 528211, tzinfo=<UTC>)

还可以查看与主题关联的条目。利用这种关联,Django能够获取与特定主题相关联的所有条目,如下所示:

>>> t.entry_set.all()
<QuerySet [<Entry: Entry object>, <Entry: Entry object>, <Entry: Entry object>]>

注意: 每次修改模型后,都要重启shell, 这样才能看到修改的效果。要退出shell会话,可按Ctrl+D ,如果是Windows系统,按Ctrl+Z,再按回车键。

3. 创建网页:学习笔记主页

使用Django创建网页过程通常分三个阶段:定义URL、编写视图和编写模板。每个URL都被映射到特定视图——视图函数获取并处理网页所需的数据。

3.1 映射URL

用户通过在浏览器中输入URL以及单击链接来请求网页,因此我们需要确定项目需要哪些URL。主页的URL最重要,它是用户用来访问项目的基础URL。当前,基础URL(http://localhost:8000/)返回默认的Django网站,让我们知道正确地建立了项目。我们将修改这一点,将这个基础URL映射到“学习笔记”的主页。

打开项目文件夹learning_log中的文件urls.py,看到如下代码:

urls.py

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
]

我们需要包含learning_logs的URL:

from django.conf.urls import url, include
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('learning_logs.urls', namespace='learning_logs')),
]

添加了一行代码来包含模块learning_logs.urls。这行代码包含实参namespace。默认的urls.py包含在文件夹learning_log中,现在我们需要在文件夹learning_logs中创建另一个urls.py文件:

urls.py

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views

urlpatterns = [
    # 主页
    url(r'^$', views.index, name='index')
]

3.2编写视图

视图函数接收请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器——这通常是使用定义了网页是什么样的模版实现的。

learning_logs中的文件views.py是你执行命令python manage.py startapp时自动生成的, 其内容如下:

views.py

from django.shortcuts import render

# Create your views here.

当前这个文件只导入了函数render(), 它根据视图提供的数据渲染响应。如何为主页编写视图,代码如下:

from django.shortcuts import render

# Create your views here.
def index(request):
    """学习笔记的主页"""
    return render(request, 'learning_logs/index.html')

3.3编写模版

模版定义了网页的结构。模版指定了网页时什么样的,而每当网页被请求时,Django将填入相关的数据。模板让你能够访问视图提供的任何数据。我们的主页视图没有提供任何数据,因此模板非常简单。

在文件夹learning_logs中新建一个文件夹,并将其命名为templates。在文件夹templates中,再新建一个文件夹,并将其命名为learning_logs。在最里面的文件夹learning_logs中,新建一个文件,并将其命名为index.html,再在这个文件中编写如下代码:

index.html

<p>Learning Log</p>

<p>Learning Log helps you keep track of your leanring, for any topic you're learning about.</p>

现在请求这个项目的基础URL–http://localhost:8000/,将看到刚才创建的网页,而不是默认的Django网页。如下图所示:

4. 创建其他网页

制定创建网页的流程后,可以开始扩充“学习笔记”项目了。创建两个显示数据的网页,其中一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页,我们都将指定URL模式,编写一个视图函数,并编写一个模板。

4.1 模板继承

1. 父模板

首先创建一个名为base.html的模板,并将其存储在index.html所在目录中。这文件包含所有页面的元素;其他模板都继承base.html。

base.html

<p>
    <a href = "{% url 'learning_logs:index' %}">Learning Log</a>
</p>

{% block content %}{% endblock content %}

2. 子模板

重新编写index.html,使其继承base.html,如下所示:

index.html

<p>Learning Log</p>
{% extends "learning_logs/base.html" %}

{% block content %}
<p>Learning Log helps you keep track of your leanring, for any topic you're learning about.</p>
{% endblock content %}

4.2 显示所有主题的页面

1. URL模式

定义显示所有主题的页面的URL。通常,使用一个简单的URL片段来指出网页显示的信息;这里使用topics, 因此URL http://localhost:8000/topics/将返回显示所有主题的页面。先修改learning_logs/urls.py:

urls.py

"""定义learning_logs的URL模式"""

from django.conf.urls import url

from . import views


urlpatterns = [
    # 主页
    url(r'^$', views.index, name='index'),

    # 显示所有的主题
    url(r'^topics/$', views.topics, name='topics'),
]

2. 视图

在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')

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by('date_added')
    context = {'topics':topics}
    return render(request, 'learning_logs/topics.html', context)

3. 模板

创建一个文件,命名为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。 单击这个链接,将看到如下图所示的网页:

4.3 显示特定主题的页面

1. URL模式

显示特定主题的页面的UR L模式与前面的所有URL模式稍有不同,因为它将使用主题的id属性来指出请求的是哪个主题。

修改learning_logs/urls.py:

urls.py


---snip---
urlpatterns = [
   ---snip--

  # 显示特定主题
    url(r'^topics/(?P<topic_id>\d+)/$', views.topic, name='topic'),
]

2. 视图

函数topic()需要从数据库中获取指定的主题以及与之相关联的所有条目,如下所示:

views.py

---snip---
def topic(request):
    """显示单个主题机器所有的条目"""
    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

---snip---
{% for topic in topics %}
<li><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></li>
    {% empty %}
---snip---

刷新显示所有主题的页面,再单击其中一个主题,页面如下图所示: