资讯专栏INFORMATION COLUMN

Python学习之路17-Django入门

shadowbook / 2321人阅读

摘要:编程从入门到实践笔记。执行命令后,项目的根目录下会多出一个名为的数据库文件。下面创建一个主题类用户学习的主题返回模型的字符串表示类是中的一个定义了模型基本功能的类。这种交互式环境称为,常用语测试项目和排除故障。

《Python编程:从入门到实践》笔记。
从本篇开始将是该书的最后一个项目,将用3篇文章来介绍Django的基础。完成一个“学习笔记”的小网站。
1. 前言

在本篇中,我们将:

用Django来开发一个名为“学习笔记”(Learning Log)的项目;

为这个项目制定规范,然后为应用程序使用的数据定义模型;

使用Django的管理系统来输入一些初试数据,再学习编写视图和模板,让Django能够为我们的网站创建网页。

不过在开始之前,请先新建一个虚拟环境并安装Django。如果没有虚拟环境,通过pip安装的所有库都会保存到python的site-packages目录下。开发多个项目时,都会用同一个python,而某些项目并不需要其中的所有第三方库,但如果将这些不需要库的删除,又会影响到其他项目。而且,如果A项目需要Django2.0.4,B项目需要Django2.0.0,这该怎么办?此时就需要虚拟环境。它其实就相当于一个新的文件夹,将新项目与其他项目隔离,本项目的库不与其他项目的库相关联,类似于操作系统的多用户概念。

如果使用的是Python 3,可以使用命令:

python -m venv ll_env

如果该命令不成功,可能是没有安装virtualenv模块:

pip install virtualenv

然后创建并激活虚拟环境:

virtualenv ll_env

# linux:
source ll_env/bin/activate
# windows:
ll_envScriptsactivate

# 停用:
deactivate

管理虚拟环境的库还有很多,有兴趣的话可以到网上搜一搜。

如果你使用的是新版的PyCharm,那么它在新建项目的时候默认就会创建新的虚拟环境。

激活虚拟环境后就可以安装Django了:

pip install django
2. 建立项目 2.1 在Django中创建项目 2.1.1 生成项目

在虚拟环境中执行如下命令:

# 主要最后有个实心句号!
# 这个句点让新项目使用合适的目录结构,这样开发完成后可以轻松地将应用程序部署到服务器
django-admin startproject learning_log .

执行上述命令后,将多出一个manage.py文件和一个learning_log文件夹,当然还有本身的一个ll_env文件夹。

而在learning_log文件夹中,又有四个文件:__init__.pysettings.pyurls.pywsgi.py

manage.py是一个简单的程序,它接收命令并将其交给Django的相关部分去运行;

settings.py指定Django如何与你的系统交互以及如何管理项目,其实就是配置文件;

urls.py告诉Django应创建哪些网页来响应浏览器请求;

wsgi.py是web server gateway interface(Web服务器网关接口)的缩写,帮助Django提供它创建的文件。

至于__init__.py,它是个空文件,Python的每个模块下必须要有这个文件。

2.1.2 创建数据库

Django将大部分与项目相关的信息都存储在数据库中,所有还需要创建一个供Django使用的数据库。依然是在虚拟环境下执行如下命令:

python manage.py migrate

在PyCharm中的话,可以通过点击工具栏Tools中的Run manage.py Task(Ctrl+Alt+R),在弹出的命令行中直接输入原命令中manage.py后面的部分,后面的命令也可以这样执行([appname]是自动提示)。

"migrate"这个单词其实是迁移的意思,并不是“创建(create)”。之所以使用这个词,是因为一般将修改数据库的过程称为迁移数据库(笔者数据库学得渣,这段解释直接从书里照搬的,希望哪位大神在留言区解释一波)。如果是刚创建的项目,并且第一次执行,将会得到如下输出:

Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  -- snip --
  Applying sessions.0001_initial... OK

从第2行结果可以看出,Django将创建和修改数据库看做是对数据库的迁移,Apply all migrations确保数据库结构与当前代码匹配(比如你修改了类的结构,添加了属性,这就相当于修改了数据表)。

执行命令后,项目的根目录下会多出一个名为db.sqlite3的数据库文件。SQLite是一种使用单个文件的轻量级数据库,常用于开发简单应用程序,它让你不用太关注数据库管理的问题。

2.1.3 运行项目

依然在项目的虚拟环境下输入如下命令:

python manage.py runserver

得到如下输出:

Performing system checks...

System check identified no issues (0 silenced).
April 21, 2018 - 20:46:48
Django version 2.0.4, using settings "learning_log.settings"
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

现在在浏览器中地址栏输入localhost:8000 (或者127.0.0.1:8000),将得到如下页面:

这是最新版的Django的默认启动界面。

2.2 创建应用程序(APP) 2.2.1 创建模型

Django项目由一系列应用程序组成,它们协同工作,让项目成为一个整体。我们在项目根目录下执行如下命令,创建一个名为learning_logs的应用程序:

python manage.py startapp learning_logs

执行命令后,根目录下会多出一个名为learning_logs的文件夹(笔者第一次接触Django的时候发现这玩意儿居然叫做APP,和平时用的手机上的各种APP相差也太大了,很不适应),它的结构如下:

重要的是其中的models.pyadmin.pyviews.py文件,我们将使用models.py来定义我们要在应用程序中管理的数据。另外两个文件稍后再介绍。

打开models.py文件,发现其中自带两行代码:

from django.db import models

# Create your models here.

在代码层面,模型就是一个类,和之前的文章中的类一样,包含属性和方法。下面创建一个“主题”类:

from django.db import models

class Topic(models.Model):
    """用户学习的主题"""
    text = models.CharField(max_length=200)
    date_added = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        """返回模型的字符串表示"""
        return self.text

Model类是Django中的一个定义了模型基本功能的类。Topic类只有两个属性textdate_added。模型中如CharFieldDateTimeField这些字段还有很多,Django自动根据数据库的不同调用不同的SQL语句创建数据表,即屏蔽底层数据库的差异。

同时还重写了__str__()方法,之所以说“重写”是因为每个类都有这个方法,当直接将一个类A放入print()之类的语句中时,就会调用A__str__()方法。如果没有重写这个方法,一般会输出这个对象的内存地址之类的,大家可以苏随便写个类试一试。

2.2.2 激活模型

使用模型前,必须将APP包含到项目中,打开settings.py文件,将APP添加到INSTALLED_APPS中:

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",
    # 添加你的app,上面的都是自带的
    "learning_logs.apps.LearningLogsConfig",
]

这里有个需要注意的地方,由于这本书的2016年出版的,当时Django还没有到达2.0版本,所以在书中,注册APP是这样写的:

INSTALLED_APPS = [
    -- snip --
    # 添加你的app,上面的都是自带的
    "learning_logs",
]

现在官方文档中是按照第一种方式注册APP,并且,最新版的Django在新建APP后,在APP的目录下还多了一个apps.py文件,该文件默认有一个根据APP名称创建的类,此处为LearningLogsConfig,内容如下:

from django.apps import AppConfig

class LearningLogsConfig(AppConfig):
    name = "learning_logs"

回到主线,在终端中输入:

python manage.py makemigrations learning_logs

# 输出
Migrations for "learning_logs":
  learning_logsmigrations001_initial.py
    - Create model Topic

makemigrationsDjango确定该如何修改数据库,使其能够存储与我们定义的新模型相关联的数据。从输出可以看出,在APP目录下的migrations文件夹中创建了一个名为0001_initial.py的迁移文件,该文件将在数据库中为模型Topic创建一个表。

最后,在命令行中输入:

python manage.py migrate

# 输出:
Running migrations:
  Applying learning_logs.0001_initial... OK

总结:每当需要修改模型时,都采取如下三个步骤:修改models.py,对你的APP调用makemigrations,让Django迁移项目migrate

2.2.3 Django管理网站

Django自带管理后台。首先为网站创建一个超级用户。在中断输入:

python manage.py createsuperuser

随后按提示输入用户名和密码即可,邮箱地址可以留空(直接回车)。

Django自动在管理网站中添加一些模型,如UserGroup,但对于我们创建的模型,必须手工注册。

注意前面提到的和models.py在同一目录的admin.py文件,这就是注册自行编写的模型的地方,在该文件中加入后两行代码:

# 第一行代码是自带的
from django.contrib import admin
from learning_logs.models import Topic

admin.site.register(Topic)

现在登录Django自带的网站管理页面 http://localhost:8000/admin/ 登录刚才创建的超级用户和密码后将出现如下界面:

在这里你可以管理用户和组,以及和模型Topic相关的数据。

现在先手动添加两个主题:点击Add创建ChessRock Climbing

2.2.4 定义模型Entry

为使能在每个主题下添加条目,需要定义Entry模型,EntryTopic的关系是多对一。同样是在models.py中添加模型:

from django.db import models

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):
        """返回模型的字符串表示"""
        # 由于条目包含的文本可能很长,只显示前50个字符
        return self.text[:50] + "..."

注意其中嵌套了一个Meta类,它用于管理模型的额外信息。它让我们能够设置一个特殊属性,让Django在需要时使用Entries来表示多个条目。如果没有这个类,Django将使用Entrys来表示多个条目(保证英语语法正确......不得不说,本书作者还是很细心的)。

添加了新模型,所以需要再次迁移数据库,过程就是前面讲的三个步骤中的后两步。然后在admin.py中注册Entry

为了让这个网站有一些初试数据,添加三个条目:两个Chess的,一个Rock Climbing的。在管理页面中点击EntriesAdd按钮,你将看到一个下拉列表,用于选择Topic,还有个文本框,用于输入内容。随便输入一点内容就可以,具体内容不再详细列出。

2.2.5 Django shell

输入一些数据后,可通过交互式终端会话以编程方式查看这些数据。这种交互式环境称为Django shell,常用语测试项目和排除故障。以下是在shell中的一些操作:

(ll_env)learning_log$ python manage.py shell   # 启动shell
>>> from learning_logs.models import Topic
>>> Topic.objects.all()   # 获得模型Topic的所有实例
, ]>  # 返回了一个查询集QuerySet

>>> topics = Topic.objects.all()  # 查询每个Topic对象
>>> for topic in topics:
...     print(topic.id, topic)
...
1 Chess
2 Rock Climbing

>>> t = Topic.objects.get(id=1) # 根据id查看Chess模型的具体内容
>>> t.text
"Chess"
>>> t.date_added
datetime.datetime(2018, 4, 22, 2, 4, 3, 723045, tzinfo=)

>>> t.entry_set.all()  # 查看该主题下的所有条目,通过外键查询,进行了人为换行
,
]>

为了通过外键获取数据,注意查询时的语法:模型小写名称+下划线+set,如第19行代码。编写用户可请求的网页时将使用这样的语法。如果代码在shell中的行为符合预期,那么它们在项目文件中也能正确工作。

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

这一部分,书中内容和新版的Django出入比较多。

3.1 映射URL

Django创建网页的过程通常分三个阶段:定义URL、编写视图和编写模板

URL模式描述了URL是如何设计的,让Django知道如何将浏览器请求与网站URL匹配,以确定返回哪个网页。每个URL都被映射到特定的视图——视图函数获取并处理网页所需的数据。视图函数通常调用一个模板,后者生成浏览器能够理解的网页。

目前,基础URL(http://localhost:8000/ )返回默认的Django页面,现在修改这个映射,将其映射到我们自己编写的主页。

打开learning_log文件夹中的urls.py,将看到如下内容:

from django.contrib import admin
from django.urls import path

urlpatterns = [
    path("admin/", admin.site.urls),
]

# 书中的内容和实际的内容有些出入,以下是书中内容:
# 新版Django简化了URL路由写法
# from django.conf.urls import include, url
# from django.contrib import admin
# 
# from django.conf.urls import include, url
# from django.contrib import admin
#
# urlpatterns = [
#    url(r"^admin/", include(admin.site.urls)),
# ]

变量urlpatterns包含项目中的APP的URL,admin.site.urls模块定义了可在管理网站中请求的所有URL。现在添加代码:

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("learning_logs.urls")),
    # 书中代码:
    # url(r"", include("learning_logs.urls", namespace="learning_logs")),
]

注意:书中在此处的include()函数中传入了关键字参数namespace="learning_logs",但在新版中,命名空间(namespace)是在APP的urls.py中设置的:在urlpatterns变量前新建一个值为"learning_logs"app_name变量。

namespacelearning_logs的URL同项目中的其他URL区分开,对项目进行扩展时,这样做十分有用。

还需要在learning_logs中创建另一个urls.py文件:

"""定义learning_logs的URL模式"""
from django.urls import path
# 从当前的urls.py模块所在的文件夹中导入视图
from . import views

# 该变量包含该APP中可请求的网页
urlpatterns = [
    # 主页
    path("", views.index, name="index"),
]

path()的第一个参数是正则表达式,第二个参数是要调用的视图函数(当请求的URL和第一个参数匹配时调用),第三个参数为这个URL模式指定一个名字,相当于将这个模式保存在变量index中,以后每当需要提供这个主页的连接时都使用这个名字,而不用再编写URL。

3.2 编写视图

视图函数接收请求中的信息,准备好生成网页所需的数据,再将这些数据发送给浏览器,在发送之前,还套用了网页的模板(如果有模板的话)。

当我们在创建APP时,它的文件夹中有一个views.py文件,该文件默认只有一个导入语句,导入了函数render(),现在编写这个文件:

from django.shortcuts import render

def index(request):
    """学习笔记主页"""
    # 将请求的数据套用到模板中,然后返回给浏览器
    # 第一个参数是原始请求对象,第二是可用于创建网页的模板
    return render(request, "learning_logs/index.html")
3.3 编写模板

模板定义了网页的结构,指定了网页时什么样的。每当网页被请求时,Django将填入相关的数据。模板中能访问视图提供的任何数据。

现在创建上面代码中的index.html模板:在learning_logs文件夹中新建一个templates文件夹,再在这个文件夹中新建一个和APP同名的文件夹,即learning_logs文件夹,最后,在这个learning_logs文件夹中新建index.html文件。看起来好像有点多余,但这是Django能够解析的目录结构。index.html文件的内容如下:

Learning Log

Learning Log helps you keep track of your learning, for any topic your"re learning about.

现在当你访问http://localhost:8000 时,将看到如下主页:

创建网页的过程看起来可能很复杂,实际上这就是一个简单的MVCModelViewController)模型,但在Django中被称为MVTModelViewTemplate

这种方式使代码结构清晰,修改方便,也让我们能各司其职,比如,数据库专家就专注于Model,程序员专注于ViewWeb设计人员专注于Template

4. 创建其他网页

我们继续扩展我们的项目。创建两个用于显示数据的网页,其中给一个列出所有的主题,另一个显示特定主题的所有条目。对于每个网页都指定URL模式,编写一个视图函数,并编写一个模板。但这么做之前,我们先创建一个父模板,项目中的其他模板都将继承它。

4.1 模板继承 4.1.1 父模板base.html

Learning Log

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

该文件存储在index.html所在的目录中。该文件包含所有页面都有的元素,其他模板继承自它。目前为止,所有页面共有的元素还只有顶端的标题。

我们将这个标题设置为到主页的链接。为了创建这个链接,使用了模板标签(花括号加百分号的组合),其中learning_logs是项目的命名空间,index就是这个项目主页的URL模式名(注意翻看前面小节的代码)。

还创建了一个块标签(block),这个块名为content,是一个占位符,其中包含的信息将由子模板指定。

4.1.2 子模板

从新编写index.html,使其继承base.html

{% extends "learning_logs/base.html" %}

{% block content %}
    

Learning Log helps you keep track of your learning, for any topic you"re learning about.

{% endblock content %}

第1行代码导入父模板中的所有内容;extends后面跟父模板所在的路径,虽然这俩文件都在同一目录下,但不能省略掉前面的learning_logs/

3到6行代码插入了一个名为content的块标签,不是从父模板继承的内容都包含在content块中。第6行代码指出内容定义的结束位置。

注意:在大型项目中,通常有一个用于整个网站的父模板base.html,且网站的每个主要部分都有一个父模板。每个主要部分的父模板又都继承自base.html,而网站的每个网页都继承相应部分的父模板。

4.2 主题页面

该页面显示用户创建的所有主题。他是第一个需要使用数据的页面。

4.2.1 定义URL模式

在APP的urls.py中添加能转到topics.html的URL模式

urlpatterns = [
    # 主页
    path("", views.index, name="index"),
    path("topics/", views.topics, name="topics")
]
4.2.2 添加视图

views.py中添加相应的函数:

from django.shortcuts import render
from .models import Topic

def index(request):
    -- snip --

def topics(request):
    """显示所有的主题"""
    topics = Topic.objects.order_by("date_added")
    # 一个上下文字典,传递给模板
    context = {"topics": topics}
    return render(request, "learning_logs/topics.html", context)
4.2.3 创建模板

创建一个模板,用于显示所有的主题:

{% extends "learning_logs/base.html" %}

{% block content %}
    

Topics

    {% for topic in topics %}
  • {{ topic }}
  • {% empty %}
  • No topics have been added yet.
  • {% endfor %}
{% endblock content %}

在这个模板中,我们使用了一个相当于for循环的模板标签,它遍历字典context中的列表topics。并且注意,该模板中没有出现context字样,相当于模板自动从context取得topics的内容。

模板中使用的代码与Python代码存在一些重要差别:Python使用缩进来指出哪些代码行是for循环的组成部分,而在模板中,每个for循环都必须明确的指出结束为止。

要在模板中打印变量,需要将变量名用双花括号括起来,每次循环时,该代码都会被替换为topic的当前值。

还使用了空模板标签(empty),它告诉Django在列表topics为空时该怎么办。

这些花括号都不会出现在网页中,它们只是用于告诉Django我们使用了一个模板变量。

修改父模板base.html,使其包含能转到主题页面的连接(第3行为添加的代码,第2行最后添加了一个连字符):

Learning Log - Topics

现在再次输入http://localhost:8000 将看到我们添加的Topics链接,点击它,将跳转到如下页面:

4.3 特定主题页面

该页面用于显示该主题下的所有条目。和上面的步骤一样,定义URL模式,编写views.py中的处理函数,编写网页模板。

4.3.1 URL模式
urlpatterns = [
    -- snip --
    path("topics//", views.topic, name="topic"),
]
4.3.2 添加视图
def topic(request, topic_id):
    """显示单个主题及其所有的条目"""
    # 通过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)

第4,6行的代码叫做查询,如果你和笔者一样是个初学者,比起代码都写完了,最后发现查询语句有问题,再来修改,那么先在Django shell中运行下代码看看结果,这样更高效。

4.3.3 模板topic.html
{% extends "learning_logs/base.html" %}

{% block content %}

  

Topic: {{ topic }}

Entries:

    {% for entry in entries %}
  • {{ entry.date_added|date:"M d, Y H:i" }}

    {{ entry.text|linebreaks }}

  • {% empty %}
  • There are no entries for this topic yet.
  • {% endfor %}
{% endblock content %}

在Django模板中,竖线(|)表示模板过滤器(对模板变量的值进行修改的函数)。过滤器date: "M d, Y H:i"以这样的格式显示时间戳:April 22, 2018 16:09。接下来的一行显示txt的完整值,而不仅仅是entry的前50个字符。过滤器linebreaks将包含换行符的长条目转换成浏览器能够理解的格式,以免显示一个不换行的文本块。

4.3.4 修改topics.html模板
-- snip --
{% for topic in topics %}
  
  • {{ topic }}
  • {% empty %} -- snip --
    4.4 效果

    现在我们回到http://localhost:8000/topics/ 页面,随便点击一个主题,比如第一个,将得到以下界面:

    5. 小结

    上述内容主要有:

    在虚拟环境中安装Django,创建项目,并核实该项目已正确地创建;

    在项目中创建APP;

    创建表示APP数据的模型;

    创建数据库,以及在修改模型后,Django可以为迁移数据库提供什么样的帮助;

    创建超级用户,并使用管理网络输入初试数据;

    Django shell;

    定义URL、创建视图函数、网页模板(包括继承)。

    下一篇中,我们将:

    创建对用户友好而直观的网页,让用户无需通过管理网站就能添加新的主题和条目(个人感觉没必要这么做,Django自带的管理系统挺好用的),以及编辑既有的条目;

    添加一个用户注册系统,让用户能够创建账户。


    迎大家关注我的微信公众号"代码港" & 个人网站 www.vpointer.net ~

    文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

    转载请注明本文地址:https://www.ucloud.cn/yun/44722.html

    相关文章

    • Python学习之路1-变量和简单数据类型

      摘要:本章主要介绍的基本数据类型以及对这些数据的简单操作。入门仪式作为一个合格的程序员,必须精通各种语言的,以下是学习的第一段代码变量变量就是数据的别称,和数学上的变量类似。 《Python编程:从入门到实践》笔记。本章主要介绍Python的基本数据类型以及对这些数据的简单操作。 1. 入门仪式 作为一个合格的程序员,必须精通各种语言的Hello, World!,以下是学习Python的第一...

      MASAILA 评论0 收藏0
    • 如何成为一名优秀的程序员

      摘要:前言罗子雄如何成为一名优秀设计师董明伟工程师的入门和进阶董明伟基于自己实践讲的知乎为新人提供了很多实用建议,他推荐的罗子雄如何成为一名优秀设计师的演讲讲的非常好,总结了设计师从入门到提高的优秀实践。 前言 罗子雄:如何成为一名优秀设计师 董明伟:Python 工程师的入门和进阶 董明伟基于自己实践讲的知乎live为Python新人提供了很多实用建议,他推荐的罗子雄:如何成为一名优秀...

      keelii 评论0 收藏0
    • 一名爬虫工程师的运维入门之路:dnspython学习笔记

      摘要:通过域名,最终得到该域名对应的地址的过程叫做域名解析或主机名解析。协议运行在协议之上,使用端口号。在文档中对有规范说明,对的动态更新进行说明,对查询的反向缓存进行说明。 你们是否跟我一样,不知道什么是DNS,在这里,首先普及一下什么是DNS DNS(Domain Name System,域名系统),万维网上作为域名和IP地址相互映射的一个分布式数据库,能够使用户更方便的访问互联网...

      LiangJ 评论0 收藏0

    发表评论

    0条评论

    最新活动
    阅读需要支付1元查看
    <