《老鸟python 系列》视频上线了,全网稀缺资源,涵盖python人工智能教程,爬虫教程,web教程,数据分析教程以及界面库和服务器教程,以及各个方向的主流实用项目,手把手带你从零开始进阶高手之路!点击 链接 查看详情

第一个应用程序

阅读:378166881    分享到

我们假定你现在已经安装上数据库管理系统,并且确认数据库连接正常工作了,让我们来创建一个 Django app-一个包含模型,视图和 Django 代码,并且形式为独立 Python 包的完整 Django 应用。

创建app

在这里要先解释一些术语,初学者可能会混淆它们。在第二章我们已经创建了 project , 那么 projectapp 之间到底有什么不同呢?它们的区别就是一个是配置另一个是 代码:

一个 project 包含很多个 Django app 以及对它们的配置。技术上,project 的作用是提供配置文件,比方说哪里定义数据库连接信息、安装的 app 列表、TEMPLATE_DIRS 等等。一个 app 是一套 Django 功能的集合,通常包括模型和视图,按 Python 的包结构的方式存在。例如,Django 本身内建有一些 app,例如注释系统和自动管理界面。app 的一个关键点是它们是很容易移植到其他 project 和被多个 project 复用。

对于如何架构 Django 代码并没有快速成套的规则。 如果你只是建造一个简单的 Web 站点,那么可能你只需要一个 app 就可以了;但如果是一个包含许多不相关的模块的复杂的网站,例如电子商务和社区之类的站点,那么你可能需要把这些模块划分成不同的 app,以便以后复用。

不错,你可以不用创建 app,这一点应经被我们之前编写的视图函数的例子证明了。在那些例子中,我们只是简单的创建了一个称为 views.py 的文件,编写了一些函数并在 URLconf 中设置了各个函数的映射。这些情况都不需要使用 apps。

但是,系统对app有一个约定: 如果你使用了 Django 的数据库层(模型),你必须创建一个 Django app。模型必须存放在 apps 中。因此,为了开始建造我们的模型,我们必须创建一个新的 app。

mysite 项目文件下输入下面的命令来创建books app:

python manage.py startapp books

这个命令并没有输出什么,它只在 mysite 的目录里创建了一个 books 目录。让我们来看看这个目录的内容:

books/
    migrations
        __init__.py
    __init__.py
    admin.py
    apps.py
    models.py
    tests.py
    views.py

这个目录包含了这个 app 的模型和视图。

我们查看一下 models.pyviews.py 文件的内容。它们都是空的,除了 models.py 里有一个 import。这就是你 Django app 的基础。

在 Python 代码里定义模型

我们早些时候谈到。MTV 里的M代表模型。Django 模型是用 Python 代码形式表述的数据在数据库中的定义。对数据层来说它等同于 CREATE TABLE 语句,只不过执行的是 Python 代码而不是 SQL,而且还包含了比数据库字段定义更多的含义。Django 用模型在后台执行 SQL 代码并把结果用 Python 的数据结构来描述。Django 也使用模型来呈现 SQL 无法处理的高级概念。

如果你对数据库很熟悉,你可能马上就会想到,用 Python SQL 来定义数据模型是不是有点多余?Django 这样做是有下面几个原因的:

自省(运行时自动识别数据库)会导致过载和有数据完整性问题。为了提供方便的数据访问 API,Django 需要以 某种方式 知道数据库层内部信息,有两种实现方式。第一种方式是用 Python 明确地定义数据模型,第二种方式是通过自省来自动侦测识别数据模型。第二种方式看起来更清晰,因为数据表信息只存放在一个地方-数据库里,但是会带来一些问题。首先,运行时扫描数据库会带来严重的系统过载。 如果每个请求都要扫描数据库的表结构,或者即便是 服务启动时做一次都是会带来不能接受的系统过载。 (有人认为这个程度的系统过载是可以接受的,而 Django 开发者的目标是尽可能地降低框架的系统过载)。第二,某些数据库,尤其是老版本的 MySQL,并未完整存储那些精确的自省元数据。编写 Python 代码是非常有趣的,保持用 Python 的方式思考会避免你的大脑在不同领域来回切换。尽可能的保持在单一的编程环境/思想状态下可以帮助你提高生产率。不得不去重复写 SQL,再写 Python 代码,再写 SQL,…,会让你头都要裂了。把数据模型用代码的方式表述来让你可以容易对它们进行版本控制。这样,你可以很容易了解数据层 的变动情况。SQL 只能描述特定类型的数据字段。例如,大多数数据库都没有专用的字段类型来描述 Email 地址、URL。而用 Django 的模型可以做到这一点。好处就是高级的数据类型带来更高的效率和更好的代码复用。SQL 还有在不同数据库平台的兼容性问题。发布 Web 应用的时候,使用 Python 模块描述数据库结构信息可以避免为 MySQL, PostgreSQL, and SQLite 编写不同的 CREATE TABLE

当然,这个方法也有一个缺点,就是 Python 代码和数据库表的同步问题。如果你修改了一个 Django 模型,你要自己来修改数据库来保证和模型同步。我们将在稍后讲解解决这个问题的几种策略。

最后,我们要提醒你 Django 提供了实用工具来从现有的数据库表中自动扫描生成模型。这对已有的数据库来说是非常快捷有用的。

第一个模型

在本章和后续章节里,我们把注意力放在一个基本的 书籍/作者/出版商 数据库结构上。我们这样做是因为,这是一个众所周知的例子,很多 SQL 有关的书籍也常用这个举例。你现在看的这本书也是由作者创作再由出版商出版的哦!

我们来假定下面的这些概念、字段和关系:

  • 一个作者有姓,有名及 email 地址。

  • 出版商有名称,地址,所在城市,省,国家,网站。

  • 书籍有书名和出版日期。它有一个或多个作者(和作者是多对多的关联关系[many-to-many]),只有一个出版商(和出版商是一对多的关联关系[one-to-many],也被称作外键[foreign key])

第一步是用 Python 代码来描述它们。打开由startapp 命令创建的models.py 并输入下面的内容:

from django.db import models

class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    city = models.CharField(max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()

class Author(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=40)
    email = models.EmailField()

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)
    publication_date = models.DateField()

让我们来快速讲解一下这些代码的含义。首先要注意的事是每个数据模型都是 django.db.models.Model 的子类。它的父类 Model 包含了所有必要的和数据库交互的方法,并提供了一个简洁漂亮的定义数据库字段的语法。信不信由你,这些就是我们需要编写的通过 Django 存取基本数据的所有代码。

每个模型相当于单个数据库表,每个属性也是这个表中的一个字段。属性名就是字段名,它的类型(例如 CharField )相当于数据库的字段类型(例如 varchar)。例如, Publisher 模块等同于下面这张表(用 Mysql 的 CREATE TABLE 语法描述):

CREATE TABLE "books_publisher" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(30) NOT NULL,
    "address" varchar(50) NOT NULL,
    "city" varchar(60) NOT NULL,
    "state_province" varchar(30) NOT NULL,
    "country" varchar(50) NOT NULL,
    "website" varchar(200) NOT NULL
);

事实上,正如过一会儿我们所要展示的,Django 可以自动生成这些 CREATE TABLE 语句。

“每个数据库表对应一个类”这条规则的例外情况是多对多关系。在我们的范例模型中,Book 有一个 多对多字段 叫做 authors 。 该字段表明一本书籍有一个或多个作者,但 Book 数据库表却并没有 authors 字段。 相反,Django 创建了一个额外的表(多对多连接表)来处理书籍和作者之间的映射关系。

最后需要注意的是,我们并没有显式地为这些模型定义任何主键。除非你单独指明,否则 Django 会自动为每个模型生成一个自增长的整数主键字段,每个 Django 模型都要求有单独的主键 id

模型安装

完成这些代码之后,现在让我们来在数据库中创建这些表。要完成该项工作,第一步是在 Django 项目中 激活 这些模型。将 books app 添加到配置文件的已安装应用列表中即可完成此步骤。

再次编辑 settings.py 文件,找到 INSTALLED_APPS 设置。INSTALLED_APPS 告诉 Django 项目哪些 app 处于激活状态。缺省情况下如下所示:

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

然后,添加 ‘books’INSTALLED_APPS 的末尾,此时设置的内容看起来应该是这样的:

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

你可能发现,我在 INSTALLED_APPS 最后一个元素 books 后面也添加一个逗号,本人喜欢这样做,这是为了避免添加新元素时忘了加逗号,而且也没什么坏处。

'books'指示我们正在编写的books app。INSTALLED_APPS 中的每个 app 都使用 Python 的路径描述,包的路径,用小数点“.”间隔。

再次编辑 settings.py 文件,找到 DATABASES 设置,DATABASES 的缺省设置如下(Django 默认连接的数据库管理系统为 sqlite3):

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

我们从控制台进入到 mysql 数据库管理系统下,使用 CREATE DATABASE dbbook; 创建一个名称为 dbbook 的数据库。

然后把 settings.py 的 DATABASES 设置改成如下:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',  # 使用 mysql 数据库管理系统
        'NAME': 'dbbook',  # 刚刚创建的数据库名称
        'USER': 'root',    # 连接数据库的用户名
        'PASSWORD': '123456',  # root 用户对应的密码
        'HOST': '',   # 主机地址,不填写的话,默认为 127.0.0.1
        'PORT': '',   # 连接 mysql 的端口,不填写的话,默认为 3306
    }
}

我们建好了模型类并且按以上操作配置好了 settings.py 文件之后,还需要安装一个和 mysql 通信的客户端模块,这个模块叫 pymysql,我们可以使用命令:pip install pymysql 进行安装,安装成功后,还需要在项目根目录的 __init__.py 文件中加上如下代码:

try:
    import pymysql
    pymysql.install_as_MySQLdb()
except:
    pass

注意:由于 pymysql 模块对 django 2.0 版本之后的支持有个 bug,你在执行下面我们马上就要用到的 check,makemigrations, sqlmigrate, migrate 等一系列命令操作中,则会报如下错误:

Traceback (most recent call last):
File "manage.py", line 21, in <module>
    main()
    File "manage.py", line 17, in main
    execute_from_command_line(sys.argv)
    ......
    query = query.decode(errors='replace')
AttributeError: 'str' object has no attribute 'decode'

解决方案如下。

找的你安装的 Django 目录下的 operations.py 文件(比如我的该文件路径在:C:\Python3\Scripts\djangoenv\Lib\site-packages\django\db\backends\mysql\ 目录下),将编码 decode 改成解码 encode

# 将代码
if query is not None:
    query = query.decode(errors='replace')
    return query

# 改为
if query is not None:
    query = query.encode(errors='replace')
    return query

现在我们可以创建数据库表了。首先,用下面的命令验证模型的有效性:

python manage.py check

check 命令检查你的模型的语法和逻辑是否正确。如果一切正常,你会看到 0 errors found 消息。如果出错,请检查你输入的模型代码。 错误输出会给出非常有用的错误信息来帮助你修正你的模型。

一旦你觉得你的模型可能有问题,运行 python manage.py check 。它可以帮助你捕获一些常见的模型定义错误。

模型确认没问题了,运行下面的命令来生成一个模型的 python 类转为 sql 语句的中间语句,这个语句很重要,该语句是把模型类转为 sql 语句或者把模型类迁移(生成)成数据库管理系统内的数据库表的前提,CREATE TABLE 语句:

python manage.py makemigrations books

执行完这个语句在,它会在 books 目录下生成一个 migrations 文件夹,里面有一个 0001_initial 文件,该文件的内容就是 python 类转为 sql 语句的中间语句。

确定模型没问题且成功生成了中间语句后,运行下面的命令来生成 CREATE TABLE 语句:

python manage.py sqlmigrate books 0001_initial

在这个命令行中, books 是 app 的名称。和你运行 manage.py startapp 中的一样。执行之后,输出如下:

BEGIN;
CREATE TABLE "books_publisher" (
    "id" serial NOT NULL PRIMARY KEY,
    "name" varchar(30) NOT NULL,
    "address" varchar(50) NOT NULL,
    "city" varchar(60) NOT NULL,
    "state_province" varchar(30) NOT NULL,
    "country" varchar(50) NOT NULL,
    "website" varchar(200) NOT NULL
)
;
CREATE TABLE "books_author" (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(40) NOT NULL,
    "email" varchar(75) NOT NULL
)
;
CREATE TABLE "books_book" (
    "id" serial NOT NULL PRIMARY KEY,
    "title" varchar(100) NOT NULL,
    "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id") DEFERRABLE INITIALLY DEFERRED,
    "publication_date" date NOT NULL
)
;
CREATE TABLE "books_book_authors" (
    "id" serial NOT NULL PRIMARY KEY,
    "book_id" integer NOT NULL REFERENCES "books_book" ("id") DEFERRABLE INITIALLY DEFERRED,
    "author_id" integer NOT NULL REFERENCES "books_author" ("id") DEFERRABLE INITIALLY DEFERRED,
    UNIQUE ("book_id", "author_id")
)
;
CREATE INDEX "books_book_publisher_id" ON "books_book" ("publisher_id");
COMMIT;

注意:

  • 自动生成的表名是 app 名称(books)和模型的小写名称(publisherbookauthor )的组合。

  • 我们前面已经提到,Django为每个表格自动添加加了一个 id 主键,你可以重新设置它。

  • 按约定,Django 添加 "_id" 后缀到外键字段名。你猜对了,这个同样是可以自定义的。

  • 外键是用 REFERENCES 语句明确定义的。

  • 这些 CREATE TABLE 语句会根据你的数据库而作调整,这样象数据库特定的一些字段例如:(MySQL),auto_increment(mysql),serial(SQLite),都会自动生成。integer primary key 同样的,字段名称也是自动处理(例如单引号还好是双引号)。例子中的输出是基于 mysql 语法的。

sqlmigrate 命令并没有在数据库中真正创建数据表,只是把 SQL 语句段打印出来,这样你可以看到 Django 究竟会做些什么。如果你想这么做的话,你可以把那些 SQL 语句复制到你的数据库客户端执行。不过,Django 提供了一种更为简易的提交 SQL 语句至数据库的方法:migrate 命令(注意:在执行这个命令前,你要确保你已经执行过 python manage.py makemigrations 命令)。

python manage.py migrate

执行这个命令后,将看到类似以下的内容:

Creating table books_publisher
Creating table books_author
Creating table books_book
Installing index for books.Book model

migrate 命令是同步你的模型到数据库的一个简单方法。它会根据 INSTALLEDAPPS 里设置的app来检查数据库,如果表不存在,它就会创建它。需要注意的是,migrate不能_将模型的修改或删除同步到数据库;如果你修改或删除了一个模型,并想把它提交到数据库,migrate 并不会做出任何处理。

如果你再次运行 python manage.py migrate,什么也没发生,因为你没有添加新的模型或者添加新的 app。因此,运行 python manage.py migrate 总是安全的,因为它不会重复执行 SQL 语句。

如果你有兴趣,花点时间用你的 mysql 客户端登录进数据库服务器看看刚才 Django 创建的数据表。也可以执行 python manage.py dbshell ,这个命令将依据 DATABASE_SERVER 的里设置自动检测使用哪种命令行客户端。常言说,后来者居上。


如果以上内容对您有帮助,请老板用微信扫一下赞赏码,赞赏后加微信号 birdpython 领取免费视频。


登录后评论