Django学习笔记之【Django与数据库交互】

Django与数据库交互

 

Django Web的设计哲学

Django设计来鼓励松耦合和分离程序模块,改变一部分代码而不影响其它模块,例如在视图方法里,使用模板来分离业务逻辑和呈现逻辑

 

MTV开发模式

Model View Controller(MVC)软件架构模式:

l  Model,指数据访问层。

l  View,指系统中选择什么来呈现以及怎样呈现的部分。

l  Controller,则指系统中通过用户输入决定使用哪个视图及访问必要的模型的部分。

Django遵循了MVC模式,它可以被称位MVC框架,下面是M,V,C在Django中的位置:

l  M,数据据访问部分,通过Django的数据库层处理。

l  V,选择数据并决定怎样呈现的部分,通过视图和模板来处理。

l  C,控制部分通过Django框架本身的URL配置和对Python方法的调用来处理;

因为“C”是Django框架本身处理而导致Django大部分精彩的东西在于“模型”、“模板”和“视图”,所以Django被称位MTV框架:

l  M,代表模型,是数据访问层,它包含了关于数据的一切东西,怎样得到数据,怎样验证数据,它具有什么行为以及数据之间的关系。

l  T,代表模板,是展现层,它包含了呈现相关的决策,如内容怎样在Web页面中显示以及其它类型的文档。

l  V,代表视图,是业务逻辑层,它包含了访问模型的逻辑和选择合适的模板,你可以认为视图是模型和模板的桥梁。

 

Django数据库配置

(1)配置文件

数据库配置在Django配置文件里面,默认是settings.py

(2)配置项

DATABASE_ENGINE = ''

DATABASE_NAME = ''

DATABASE_USER = ''

DATABASE_PASSWORD = ''

DATABASE_HOST = ''

DATABASE_PORT = ''

(3)配置项说明

l  DATABASE_ENGINE

告诉Django使用哪个数据库引擎

l  DATABASE_NAME

告诉Django数据库名字是什么

l  DATABASE_USER

告诉Django你连接数据库的用户名

l  DATABASE_PASSWORD

告诉Django你连接数据库的密码

l  DATABASE_HOST

告诉Django你连接数据库的主机,如果你的数据库和Django安装在同一台计算机上,则这项为空。MySQL在这里很特殊,如果这项的值以'/'开头并且你使用MySQL,MySQL会通过Unix socket连接特殊的socket,例如:DATABASE_HOST = '/var/run/mysql/'。如果你使用MySQL但这项的值不是以'/'开头,那么这项的值就假设为所连接的主机

l  DATABASE_PORT

告诉Django连接数据库的端口。

(4)配置测试

运行python manage.py shell

注意:它和普通的python有一个重要的不同,这个命令告诉Django在启动shell前使用哪个settings文件。

>>> from django.db import connnection

>>> cursor = connection.cursor()

如果什么事情都没有发生,则你的数据库配置对了。

 

Django app

(1)Django app简介

Django app是一些Django代码,包括模型和视图,它们在同一个Python包下面,代表了一个完整的Django程序。

(2)Django Project和Django App的区别

l  一个Django Project是许多Django app的集合的实例,加上那些app的的配置。技术上来说,Django Project唯一的前提是它提供一个settings文件,里面定义了数据库连接信息,安装的Django app,TEMPLATE_DIRS等等。

l  一个Django app是Django的可移动功能集,通常包括模型和视图,存在于一个单独的Python包里面。例如,Django含有几个App,如commenting系统和自动的admin界面,关键要注意的是它们是可移动并且可以在不同的project重用。

(3)Django app的使用约定

没有严格的规定怎样安排和计划你的Django代码,它是很灵活的:

l  如果你在构建一个单独的网站,你可能只使用一个Django app。

l  如果你在构建一个复杂的站点,你可能想把它分成几个Django app,这样你就可以在以后分别重用他们。

l  如果你使用Django的数据库层(模型),你必须创建Django App,模型必须存在于Django App。

(4)Django app创建

python manage.py startapp books

这个命令不会造成任何输出,但它在mysite目录下创建了一个books目录,让我们看看它的内容:

books/

__init__.py

models.py

views.py

这些文件将包含这个app的模型和视图,用你最喜欢的文本编辑器看看models.py和views.py,它们都是空的,除了models.py里一个import,这是你的Django app的空白区。

 

Django模型

(1)简介

l  一个Django模型用Python代码描述了你的数据库中的数据,它是你的数据结构,相当于SQL的CREATE TABLE语句,但是在Python中它比数据库定义包含更多的内容。

l  Django在后台使用模型来执行SQL代码并返回方便的Python数据结构来表示你的数据库表的行。

l  如果你更改了Django模型,你需要在你的数据库做同样的改动做保持数据库和模型一致。

l  Django包含了一个辅助工具来通过现存的数据库生成模型,这对于迅速接管和运行遗留数据很有帮助。

(2)定义模型

使用Python代码描述上面的数据库结构,在startapp命令创建的models.py中输入下面的内容:

from django.db import models

 

class Publisher(models.Model):

name = models.CharField(maxlength=30)

address = models.CharField(maxlength=50)

city = models.CharField(maxlength=60)

state_province = models.CharField(maxlength=30)

country = models.CharField(maxlength=50)

website = models.URLField()

 

class Author(models.Model):

salutation = models.CharField(maxlength=10)

first_name = models.CharField(maxlength=30)

last_name = models.CharField(maxlength=40)

email = models.EmailField()

headshot = models.ImageField(upload_to='/tmp')

 

class Book(models.Model):

title = models.CharField(maxlength=100)

authors = models.ManyToManyField(Author)

publisher = models.ForeignKey(Publisher)

publication_date = models.DateField()

每个模型都是django.db.models.Model的子类,Model包含了让这些对象具有与数据库交互能力的机制,不管相信与否,这就是通过Django进行数据访问的所有代码。

一个模型通常与一个数据库表对应,而每个属性和数据库表的一列对应属性名对应列名,属性的类型(如CharField)对应数据库列类型。

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

我们没有在任何一个模型中显示的定义主键,除非你自己定义一个主键,Django会自动为每个模型生成一个integer主键域id,每个Django模型都必须有一个单列的主键。

(3)安装模型

1)在Django中激活这些模型,需要把books这个app添加到settings文件的apps列表,编辑settings.py,查找INSTALLED_APPS设置,INSTALLED_APPS告诉Django哪些apps是活动的。

添加'mysite.books'到INSTALLED_APPS列表,最后如下所示:

INSTALLED_APPS = (

#'django.contrib.auth',

#'django.contrib.contenttypes',

#'django.contrib.sessions',

#'django.contrib.sites',

'mysite.books',

)

INSTALLED_APPS中的每个app都用完整的Python PATH来表示,即包的PATH,用小数点分隔来指向app包。

2)Django app已经在settings文件激活,我们可以在数据库中创建表了。首通过如下的命令验证一下模型:python manage.py validate。validate命令检查我们的模型语法和逻辑正确与否。

如果一切正常,我们会看到0 errors found的信息。

3)如果你的模型是合法的,运行下面的命令,为books app的模型生成CREATE TABLE语句:python manage.py sqlall books

这个命令中,books是app的名字,运行完命令,你会看到下面的信息:

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_book" (

"id" serial NOT NULL PRIMARY KEY,

"title" varchar(100) NOT NULL,

"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),

"publication_date" date NOT NULL

);

CREATE TABLE "books_author" (

"id" serial NOT NULL PRIMARY KEY,

"salutation" varchar(10) NOT NULL,

"first_name" varchar(30) NOT NULL,

"last_name" varchar(40) NOT NULL,

"email" varchar(75) NOT NULL,

"headshot" varchar(100) NOT NULL

);

CREATE TABLE "books_book_authors" (

"id" serial NOT NULL PRIMARY KEY,

"book_id" integer NOT NULL REFERENCES "books_book" ("id"),

"author_id" integer NOT NULL REFERENCES "books_author" ("id"),

UNIQUE ("book_id", "author_id")

);

CREATE INDEX books_book_publisher_id ON "books_book" ("publisher_id");

COMMIT;

注意以下几点:

l  表明自动由app名(books)和小写的模型名-publisher,book和author组成。

l  sqlall命令事实上并没有接触数据库或建表,它仅仅将输出打印到屏幕上。

4)把这些SQL提交数据库

像下面这样运行syncdb命令:python manage.py syncdb,你会看到如下信息:

Creating table books_publisher

Creating table books_book

Creating table books_author

Installing index for books.Book model

syncdb简单的把你的模型同步到数据库,它检查数据库和你的INSTALLED_APPS中的所有app的模型,看看是否有些表已经存在,如果表不存在就创建表。

注意:syncdb不会同步改动或删除了的模型,如果你改动或删除了一个模型,syncdb不会更新数据库。

(4)数据访问基础

一旦你创建了一个模型,Django自动提供高级Python API给这些模型工作,运行python manage.py shell,然后输入下面的代码:

>>> from books.models import Publisher

>>> p = Publisher(name='Apress', address='2560 Ninth St.',

... city='Berkeley', state_province='CA', country='U.S.A.',

... website='http://www.apress.com/')

>>> p.save()

>>> p = Publisher(name="O'Reilly", address='10 Fawcett St.',

... city='Cambridge', state_province='MA', country='U.S.A.',

... website='http://www.oreilly.com/')

>>> p.save()

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[, ]

虽然只有几行代码,确达到了很多目的,精彩的部分是:

l  创建一个对象只需import合适的模型类并通过给每个域传递值来初始化它。

l  调用save()方法来将一个对象保存到数据库,后台Django在这里执行了一条INSERT SQL语句。

l  使用Publisher.objects属性从数据库得到对象,使用Publisher.objects.all()得到Publisher所有的对象列表,后台Django在这里执行了一条SELECT SQL语句。

(5)添加模型的string显示

上面的例子中,当我们打印publishers列表时我们得到的都是一些无用的信息,我们很难将Publisher对象区别开:[, ]。

我们可以通过给Publisher对象添加一个__str__()方法来轻松解决这个问题。__str__()方法告诉Python怎样显示对象的string显示,给三个模型添加__str__():

class Publisher(models.Model):

name = models.CharField(maxlength=30)

address = models.CharField(maxlength=50)

city = models.CharField(maxlength=60)

state_province = models.CharField(maxlength=30)

country = models.CharField(maxlength=50)

website = models.URLField()

def __str__(self):

return self.name

 

class Author(models.Model):

salutation = models.CharField(maxlength=10)

first_name = models.CharField(maxlength=30)

last_name = models.CharField(maxlength=40)

email = models.EmailField()

headshot = models.ImageField(upload_to='/tmp')

def __str__(self):

return '%s %s' % (self.first_name, self.last_name)

 

class Book(models.Model):

title = models.CharField(maxlength=100)

authors = models.ManyToManyField(Author)

publisher = models.ForeignKey(Publisher)

publication_date = models.DateField()

def __str__(self):

return self.title

现在,Publisher列表对象更容易理解:

>>> from books.models import Publisher

>>> publisher_list = Publisher.objects.all()

>>> publisher_list

[, ]

确认你定义的任何模型都有一个__str__()方法,不仅是使在你自己使用交互环境时更方便

也因为当Django在几个地方需要显示对象时会使用__str__()的输出。

注意:__str__()是给模型添加行为的好习惯。

(6)插入和更新数据

先使用一些关键参数创建对象实例,如下:

>>> p = Publisher(name='Apress',

... address='2855 Telegraph Ave.',

... city='Berkeley',

... state_province='CA',

... country='U.S.A.',

... website='http://www.apress.com/')

这个对象实例并没有对数据库做修改,要保存这个记录到数据库里(也就是执行 INSERT SQL 语句),调用对象的 save() 方法:

>>> p.save()

在SQL里,这大致可以转换成这样:

INSERT INTO book_publisher(name, address, city, state_province, country, website)

VALUES('Apress', '2855 Telegraph Ave.', 'Berkeley', 'CA','U.S.A.', 'http://www.apress.com/');

接下来再调用 save() 将不会创建新的记录,而只是修改记录内容(也就是执行 UPDATE SQL语句,而不是 INSERT 语句):

>>> p.name = 'Apress Publishing'

>>> p.save()

前面执行的 save() 相当于下面的SQL语句:

UPDATE book_publisher SET

name = 'Apress Publishing',

address = '2855 Telegraph Ave.',

city = 'Berkeley',

state_province = 'CA',

country = 'U.S.A.',

website = 'http://www.apress.com'

WHERE id = 52;

(7)选择对象

我们已经知道查找所有数据的方法了:

>>> Publisher.objects.all()

[, , ]

这相当于这个SQL语句:

SELECT id, name, address, city, state_province, country, website FROM book_publisher;

注意:Django在选择所有数据时并没有使用 SELECT*,而是显式列出了所有字段。SELECT* 会更慢,而且最重要的是列出所有字段遵循了Python界的一个信条:明确比不明确好。

让我们来仔细看看 Publisher.objects.all()这行的每个部分:

l  objects 是干一个管理器(manager),管理器将在附录B详细描述,在这里你只要知道它处理有关数据表的操作,特别是数据查找,所有的模型都自动拥有一个objects管理器;你可以在想要查找数据时是使用它。

l  最后,还有all()方法,这是objects管理器返回所有记录的一个方法。尽管这个对象看起来象一个列表(list),它实际是一个QuerySet对象,这个对象是数据库中一些记录的集合。附录C将详细描述QuerySet,现在,我们就先当它是一个仿真列表对象好了。

l  所有的数据库查找都遵循一个通用模式:调用模型的管理器来查找数据。

(8)数据过滤

l  如果想要获得数据的一个子集,我们可以使用filter()方法:

>>> Publisher.objects.filter(name="Apress Publishing")

[]

l  filter() 根据关键字参数来转换成WHERE SQL语句,前面这个例子相当于这样:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

WHERE name = 'Apress Publishing';

l  你可以传递多个参数到filter()来缩小选取范围:

>>> Publisher.objects.filter(country="U.S.A.", state_province="CA")

[]

l  多个参数会被转换成AND SQL语句,例如象下面这样:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

WHERE country = 'U.S.A.' AND state_province = 'CA';

l  其他的查找类型如下:

>>> Publisher.objects.filter(name__contains="press")

[]

l  在name和contains之间有双下划线,象Python自己一样,Django也使用双下划线来做一些小魔法,这个__contains 部分会被Django转换成LIKE SQL语句:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

WHERE name LIKE '%press%';

l  其他的一些查找类型有:icontains (大小写无关的 LIKE ),startswith和endswith ,还有range (SQL BETWEEN 查询)。附录C详细列出了这些类型的详细资料。

(9)获取单个对象

有时你只想获取单个对象,这个时候使用get()方法:

>>> Publisher.objects.get(name="Apress Publishing")

这样,就返回了单个对象,而不是列表(更准确的说,QuerySet)。所以,如果结果是多个对象,会导致抛出异常:

(10)数据排序

在运行前面的例子中,你可能已经注意到返回的结果是无序的,我们还没有告诉数据库怎样

对结果进行排序,所以我们返回的结果是无序的。当然,我们不希望在页面上列出的出版商的列表是杂乱无章的。

我们用 order_by() 来排列返回的数据:

>>> Publisher.objects.order_by("name")

[, , ]

跟以前的all()例子差不多,SQL语句里多了指定排序的部分:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

ORDER BY name;

多个字段也没问题:

>>> Publisher.objects.order_by("state_provice", "address")

[, , ]

我们还可以指定逆向排序,在前面加一个减号 - 前缀:

>>> Publisher.objects.order_by("-name")

[, , ]

每次都要用 order_by() 显得有点啰嗦,大多数时间你通常只会对某些字段进行排序,在这

种情况下,Django让你可以指定模型的缺省排序方式:

class Publisher(models.Model):

name = models.CharField(maxlength=30)

address = models.CharField(maxlength=50)

city = models.CharField(maxlength=60)

state_province = models.CharField(maxlength=30)

country = models.CharField(maxlength=50)

website = models.URLField()

def __str__(self):

return self.name

class Meta:

ordering = ["name"]

这个ordering = ["name"] 告诉Django如果没有显示提供order_by(),就缺省按名称排序。

Meta是什么?

Django使用内部类Meta存放用于附加描述该模型的元数据,这个类完全可以不实现,不过他

能做很多非常有用的事情。查看附录B,在Meta项下面,获得更多选项信息。

(11)排序

你已经知道怎么过滤数据了,现在让我们来排序它们。你可以同时做过滤和排序,很简单,就象这样:

>>> Publisher.objects.filter(country="U.S.A.").order_by("-name")

[, , ]

你应该没猜错,转换成SQL查询就是 WHERE 和 ORDER BY 的组合:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

WHERE country = 'U.S.A'

ORDER BY name DESC;

你可以任意把它们串起来,多长都可以,这里没有限制。

(12)限制返回的数据

另一个常用的需求就是取出固定数目的记录。想象一下你有成千上万的出版商在你的数据库

里,但是你只想显示第一个,你可以这样做:

>>> Publisher.objects.all()[0]

这相当于:

SELECT

id, name, address, city, state_province, country, website

FROM book_publisher

ORDER BY name

LIMIT 1;

还有更多,我们只是刚接触到模型的皮毛,你还必须了解更多的内容以便理解以后的范例,具体请看附录C。

(13)删除对象

要删除对象,只需简单的调用对象的 delete() 方法:

>>> p = Publisher.objects.get(name="Addison-Wesley")

>>> p.delete()

>>> Publisher.objects.all()

[, ]

你还可以批量删除对象,通过对查询的结果调用 delete() 方法:

>>> publishers = Publisher.objects.all()

>>> publishers.delete()

>>> Publisher.objects.all()

[]

注意:删除是不可恢复的,所以要小心操作!事实上,应该尽量避免删除对象,除非你确实需要删除它。数据库的数据恢复的功能通常不太好,而从备份数据恢复是很痛苦的。通常更好的方法是给你的数据模型添加激活标志。你可以只在激活的对象中查找,对于不需要的对象,将激活字段值设为 False , 而不是删除对象。这样,如果一旦你认为做错了的话,只需把标志重设回来就可以了。

(14)修改数据库表结构

syncdb 仅仅创建数据库中不存在的表,而不会同步模型的修改或者删除到数据库。如果你添加或者修改了模型的一个字段,或者删除一个模型,你必须手动改变你的数据库。下面我们看看怎么来做。

当我们处理表结构的修改时,要时刻想着Django的数据库层是如何工作的:

l  如果模型中包含一个在数据库中并不存在的字段,Django会大声抱怨的。这样当你第一次调用Django的数据库API来查询给定的表时就会出错(也就是说,它会在执行的时候出错,而不是编译的时候)。

l  Django并不关心数据库表中是否存在没有在模型中定义的列。

l  Django并不关心数据库中是否包含没有模型描述的表。

1)添加字段

当按照产品需求向一个表/模型添加字段时,Django不关心一个表的列是否在模型中定义,我们可以利用这个小技巧,先在数据库中添加列,然后再改变模型中对应的字段。

为了弄清新的数据列怎么用SQL描述,你需要查看 manage.py sqlall 的执行结果,它列出了模型中已经存在的字段。

下面是详细的步骤。

a)         把这个字段添加到你的模型中.

b)        运行 manage.py sqlall [yourapp] 会看到模型的新的 CREATE TABLE 语句。注意新的字段的列定义。

c)         启动您的数据库交互shell,执行一个 ALTER TABLE 语句,添加您的新列。

d)        (可选)用 manage.py shell 启动Python交互式shell,并通过引入模型并选择表验证新的字段已被正确添加(比如,MyModel.objects.all()[:5] )。

例如,让我们通过给 Book 模型添加一个 num_pages 字段来演示一下。

class Book(models.Model):

title = models.CharField(maxlength=100)

authors = models.ManyToManyField(Author)

publisher = models.ForeignKey(Publisher)

publication_date = models.DateField()

**num_pages = models.IntegerField(blank=True, null=True)**

def __str__(self):

return self.title

然后我们运行命令 manage.py sqlall books 来得到 CREATE TABLE 语句。它们看起来是这样的:

CREATE TABLE "books_book" (

"id" serial NOT NULL PRIMARY KEY,

"title" varchar(100) NOT NULL,

"publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),

"publication_date" date NOT NULL,

"num_pages" integer NULL

);

新加的字段SQL描述是这样的:"num_pages" integer NULL

接下来,我们启动数据库交互命令界面,执行下面的语句:

ALTER TABLE books_book ADD COLUMN num_pages integer;

执行完 ALTER TABLE 语句, 我们确认一下修改是否正确,启动Python交互界面并执行下面语句:

>>> from mysite.books.models import Book

>>> Book.objects.all()[:5]

2)删除字段

从模型里删除一个字段可要比增加它简单多了,删除一个字段仅需要做如下操作:从你的模型里删除这个字段,并重启Web服务器,使用如下面所示的命令,从你的数据库中删掉该列:

ALTER TABLE books_book DROP COLUMN num_pages;

3)删除 Many-to-Many 字段

因为many-to-many字段同普通字段有些不同,它的删除过程也不一样:

删除掉你的模型里的 ManyToManyField ,并且重启Web服务器。

使用如下面所示的命令,删除掉你数据库里的many-to-many表:

DROP TABLE books_books_publishers;

4)删除模型

完全删除一个模型就像删除一个字段一样简单,删除模型仅需要做如下步骤:

将此模型从你的 models.py 文件里删除,并且重启Web服务器。

使用如下的命令,将此表从你的数据库中删除:

DROP TABLE books_book;

你可能感兴趣的:(Django)