更改数据库模式(Database Schema)

在我们在第5章介绍 syncdb 这个命令时, 我们注意到 syncdb仅仅创建数据库里还没有的表,它 并不 对你数据模型的修改进行同步,也不处理数据模型的删除。 如果你新增或修改数据模型里的字段,或是删除了一个数据模型,你需要手动在数据库里进行相应的修改。 这段将解释了具体怎么做:

当处理模型修改的时候,将Django的数据库层的工作流程铭记于心是很重要的。

  • 如果模型包含一个未曾在数据库里建立的字段,Django会报出错信息。 当你第一次用Django的数据库API请求表中不存在的字段时会导致错误(就是说,它会在运行时出错,而不是编译时)。

  • Django关心数据库表中是否存在未在模型中定义的列。

  • Django关心数据库中是否存在未被模型表示的表格。

改变模型的模式架构意味着需要按照顺序更改Python代码和数据库。

添加字段

当要向一个产品设置表(或者说是model)添加一个字段的时候,要使用的技巧是利用Django不关心表里是否包含model里所没有的列的特性。 策略就是现在数据库里加入字段,然后同步Django的模型以包含新字段。

然而 这里有一个鸡生蛋蛋生鸡的问题 ,由于要想了解新增列的SQL语句,你需要使用Django的 manage.py sqlall命令进行查看 ,而这又需要字段已经在模型里存在了。 (注意:你并 不是非得使用与Django相同的SQL语句创建新的字段,但是这样做确实是一个好主意 ,它能让一切都保持同步。)

这个鸡-蛋的问题的解决方法是在开发者环境里而不是发布环境里实现这个变化。 (你使用的是测试/开发环境,对吧?)下面是具体的实施步骤。

首先,进入开发环境(也就是说,不是在发布环境里):

  • 在你的模型里添加字段。

  • 运行 manage.py sqlall [yourapp] 来测试模型新的 CREATE TABLE 语句。 注意为新字段的列定义。

  • 开启你的数据库的交互命令界面(比如, psqlmysql , 或者可以使用 manage.py dbshell )。 执行 ALTER TABLE 语句来添加新列。

  • 使用Python的manage.py shell,通过导入模型和选中表单(例如, MyModel.objects.all()[:5] )来验证新的字段是否被正确的添加 ,如果一切顺利,所有的语句都不会报错。

然后在你的产品服务器上再实施一遍这些步骤。

  • 启动数据库的交互界面。

  • 执行在开发环境步骤中,第三步的ALTER TABLE语句。

  • 将新的字段加入到模型中。 如果你使用了某种版本控制工具,并且在第一步中,已经提交了你在开发环境上的修改,现在,可以在生产环境中更新你的代码了(例如,如果你使用Subversion,执行svn update

  • 重新启动Web server,使修改生效。

让我们实践下,比如添加一个num_pages字段到第五章中Book模型。首先,我们会把开发环境中的模型改成如下形式:

  1. class Book(models.Model):
  2. title = models.CharField(max_length=100)
  3. authors = models.ManyToManyField(Author)
  4. publisher = models.ForeignKey(Publisher)
  5. publication_date = models.DateField()
  6. **num_pages = models.IntegerField(blank=True, null=True)**
  7. def __unicode__(self):
  8. return self.title

(注意 阅读第六章的“设置可选字段”以及本章下面的“添加非空列”小节以了解我们在这里添加blank=Truenull=True的原因。)

然后,我们运行命令manage.py sqlall books 来查看CREATE TABLE语句。 语句的具体内容取决与你所使用的数据库, 大概是这个样子:

  1. CREATE TABLE "books_book" (
  2. "id" serial NOT NULL PRIMARY KEY,
  3. "title" varchar(100) NOT NULL,
  4. "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"),
  5. "publication_date" date NOT NULL,
  6. "num_pages" integer NULL
  7. );

新加的字段被这样表示:

  1. "num_pages" integer NULL

接下来,我们要在开发环境上运行数据库客户端,如果是PostgreSQL,运行 psql,,然后,我执行如下语句。

  1. ALTER TABLE books_book ADD COLUMN num_pages integer;

添加 非NULL 字段

这里有个微妙之处值得一提。 在我们添加字段num_pages的时候,我们使用了 blank=True 和 null=True 选项。 这是因为在我们第一次创建它的时候,这个数据库字段会含有空值。

然而,想要添加不能含有空值的字段也是可以的。 要想实现这样的效果,你必须先创建 NULL 型的字段,然后将该字段的值填充为某个默认值,然后再将该字段改为 NOT NULL 型。 例如:

  1. BEGIN;
  2. ALTER TABLE books_book ADD COLUMN num_pages integer;
  3. UPDATE books_book SET num_pages=0;
  4. ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL;
  5. COMMIT;

如果你这样做,记得你不要在模型中添加 blank=True 和 null=True 选项。

执行ALTER TABLE之后,我们要验证一下修改结果是否正确。启动python并执行下面的代码:

  1. >>> from mysite.books.models import Book
  2. >>> Book.objects.all()[:5]

如果没有异常发生,我们将切换到生产服务器,然后在生产环境的数据库中执行命令ALTER TABLE 然后我们更新生产环境中的模型,最后重启web服务器。

删除字段

从Model中删除一个字段要比添加容易得多。 删除字段,仅仅只要以下几个步骤:

删除字段,然后重新启动你的web服务器。用以下命令从数据库中删除字段:
  1. ALTER TABLE books_book DROP COLUMN num_pages;

请保证操作的顺序正确。 如果你先从数据库中删除字段,Django将会立即抛出异常。

删除多对多关联字段

由于多对多关联字段不同于普通字段,所以删除操作是不同的。

从你的模型中删除ManyToManyField,然后重启web服务器。用下面的命令从数据库删除关联表:
  1. DROP TABLE books_book_authors;

像上面一样,注意操作的顺序。

删除模型

删除整个模型要比删除一个字段容易。 删除一个模型只要以下几个步骤:

从文件中删除你想要删除的模型,然后重启web 服务器models.py然后用以下命令从数据库中删除表:
  1. DROP TABLE books_book;
当你需要从数据库中删除任何有依赖的表时要注意(也就是任何与表books_book有外键的表 )。

正如在前面部分,一定要按这样的顺序做。