首页 > 代码库 > Python框架之Django学习笔记(十一)

Python框架之Django学习笔记(十一)

  话说上次说到数据库的基本访问,而数据库我们主要进行的操作就是CRUD,也即是做计算处理时的增加(Create)、读取(Retrieve)(重新得到数据)、更新(Update)和删除(Delete),俗称:增删改查。废话不多说,学习开始。

  插入和更新数据

  你已经知道怎么做了: 先使用一些关键参数创建对象实例,如下:

1 >>> p = Publisher(name=Apress,2 ...         address=2855 Telegraph Ave.,3 ...         city=Berkeley,4 ...         state_province=CA,5 ...         country=U.S.A.,6 ...         website=http://www.apress.com/)

  这个对象实例并 没有 对数据库做修改。 在调用`` save()`` 方法之前,记录并没有保存至数据库,像这样:  

1 >>> p.save()

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

INSERT INTO books_publisher    (name, address, city, state_province, country, website)VALUES    (Apress, 2855 Telegraph Ave., Berkeley, CA,     U.S.A., http://www.apress.com/);

   因为 Publisher 模型有一个自动增加的主键 id ,所以第一次调用 save() 还多做了一件事: 计算这个主键的值并把它赋值给这个对象实例: 

1 >>> p.id2 3L    # this will differ based on your own data 

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

1 >>> p.name = Apress Publishing2 >>> p.save()

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

1 UPDATE books_publisher SET2     name = Apress Publishing,3     address = 2855 Telegraph Ave.,4     city = Berkeley,5     state_province = CA,6     country = U.S.A.,7     website = http://www.apress.com8 WHERE id = 3L;

 

  注意,并不是只更新修改过的那个字段,所有的字段都会被更新。 这个操作有可能引起竞态条件,这取决于你的应用程序。

  选择对象

  当然,创建新的数据库,并更新之中的数据是必要的,但是,对于 Web 应用程序来说,更多的时候是在检索查询数据库。 我们已经知道如何从一个给定的模型中取出所有记录:

1 >>> Publisher.objects.all()2 [<Publisher: Apress>, <Publisher: OReilly>]

  这相当于这个SQL语句:

1 SELECT id, name, address, city, state_province, country, website2 FROM books_publisher;

  注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。 设计的时候就是这样:SELECT* 会更慢,而且最重要的是列出所有字段遵循了Python 界的一个信条: 明言胜于暗示。

  有关Python之禅(戒律) :-),在Python提示行输入 import this 试试看。

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

  • 首先,我们有一个已定义的模型 Publisher 。没什么好奇怪的: 你想要查找数据, 你就用模型来获得数据。
  • 然后,是objects属性。 它被称为管理器,我们将在第10章中详细讨论它。 目前,我们只需了解管理器管理着所有针对数据包含、还有最重要的数据查询的表格级操作。
  • 所有的模型都自动拥有一个 objects 管理器;你可以在想要查找数据时使用它。
  • 最后,还有 all() 方法。这个方法返回返回数据库中所有的记录。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。 附录C将详细描述QuerySet。 现在,我们就先当它是一个仿真列表对象好了。

  任何对数据库的查询操作都遵循这样的模式:即 调用绑定在该模型上的管理器(objects)的相应方法(如all)。

  数据过滤

  我们很少会一次性从数据库中取出所有的数据;通常都只针对一部分数据进行操作。 在Django API中,我们可以使用 “filter()” 方法对数据进行过滤: 

1 >>> Publisher.objects.filter(name=Apress)2 [<Publisher: Apress>]

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

1 SELECT id, name, address, city, state_province, country, website2 FROM books_publisher3 WHERE name = Apress;

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

1 >>> Publisher.objects.filter(country="U.S.A.", state_province="CA")2 [<Publisher: Apress>] 

  多个参数会被转换成 AND SQL从句, 因此上面的代码可以转化成这样:

1 SELECT id, name, address, city, state_province, country, website2 FROM books_publisher3 WHERE country = U.S.A.4 AND state_province = CA;

    注意,SQL缺省的 = 操作符是精确匹配的, 其他类型的查找也可以使用:

1 >>> Publisher.objects.filter(name__contains="press")2 [<Publisher: Apress>]

   在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的操作。这里,contains部分会被Django翻译成LIKE语句: 

1 SELECT id, name, address, city, state_province, country, website2 FROM books_publisher3 WHERE name LIKE %press%;

   其他的一些查找类型有:icontains(大小写无关的LIKE),startswithendswith, 还有range(SQLBETWEEN查询)。

  获取单个对象

  上面的例子中“ filter()” 函数返回一个记录集,这个记录集是一个列表。 相对列表来说,有些时候我们更需要获取单个的对象, “get()” 方法就是在此时使用的:

1 >>> Publisher.objects.get(name="Apress")2 <Publisher: Apress>

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

1 >>> Publisher.objects.get(country="U.S.A.")2 Traceback (most recent call last):3     ...4 MultipleObjectsReturned: get() returned more than one Publisher --5     it returned 2! Lookup parameters were {country: U.S.A.}

   如果查询没有返回结果也会抛出异常:

1 >>> Publisher.objects.get(name="Penguin")2 Traceback (most recent call last):3     ...4 DoesNotExist: Publisher matching query does not exist.

  这个 DoesNotExist 异常 是 Publisher 这个 model 类的一个属性,即 Publisher.DoesNotExist。在你的应用中,你可以捕获并处理这个异常,像这样: 

1 try:2     p = Publisher.objects.get(name=Apress)3 except Publisher.DoesNotExist:4     print "Apress isn‘t in the database yet."5 else:6     print "Apress is in the database."

 

  数据排序

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

  在你的 Django 应用中,你或许希望根据某字段的值对检索结果排序,比如说,按字母顺序。 那么,使用order_by() 这个方法就可以搞定了。

1 >>> Publisher.objects.order_by("name")2 [<Publisher: Apress>, <Publisher: OReilly>]

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

1 SELECT id, name, address, city, state_province, country, website2 FROM books_publisher3 ORDER BY name;

  如果需要以多个字段为标准进行排序(第二个字段会在第一个字段的值相同的情况下被使用到),使用多个参数就可以了,如下:

1 >>> Publisher.objects.order_by("state_province", "address")2  [<Publisher: Apress>, <Publisher: OReilly>]

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

1 >>> Publisher.objects.order_by("-name")2 [<Publisher: OReilly>, <Publisher: Apress>]

  尽管很灵活,但是每次都要用 order_by() 显得有点啰嗦。 大多数时间你通常只会对某些 字段进行排序。 在这种情况下,Django让你可以指定模型的缺省排序方式:

 1 class Publisher(models.Model): 2     name = models.CharField(max_length=30) 3     address = models.CharField(max_length=50) 4     city = models.CharField(max_length=60) 5     state_province = models.CharField(max_length=30) 6     country = models.CharField(max_length=50) 7     website = models.URLField() 8  9     def __unicode__(self):10         return self.name11 12     class Meta:13         ordering = [name]

 

  现在,让我们来接触一个新的概念。 class Meta,内嵌于 Publisher 这个类的定义中(如果 class Publisher是顶格的,那么 class Meta 在它之下要缩进4个空格--按 Python 的传统 )。你可以在任意一个 模型 类中使用 Meta 类,来设置一些与特定模型相关的选项。 在 附录B 中有 Meta 中所有可选项的完整参考,现在,我们关注 ordering 这个选项就够了。 如果你设置了这个选项,那么除非你检索时特意额外地使用了 order_by(),否则,当你使用 Django 的数据库 API 去检索时,Publisher对象的相关返回值默认地都会按 name 字段排序。

  更新多个对象

  在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。

  例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如:

1 >>> p = Publisher.objects.get(name=Apress)2 >>> p.name = Apress Publishing3 >>> p.save()

  这等同于如下SQL语句:

SELECT id, name, address, city, state_province, country, websiteFROM books_publisherWHERE name = Apress;UPDATE books_publisher SET    name = Apress Publishing,    address = 2855 Telegraph Ave.,    city = Berkeley,    state_province = CA,    country = U.S.A.,    website = http://www.apress.comWHERE id = 3L;

  在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:

1 >>> Publisher.objects.filter(id=52).update(name=Apress Publishing)

  与之等同的SQL语句变得更高效,并且不会引起竞态条件。  

1 UPDATE books_publisher2 SET name = Apress Publishing3 WHERE id = 3L;

  update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’: 

1 >>> Publisher.objects.all().update(country=USA)2 2

   update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。

  删除对象

  删除数据库中的对象只需调用该对象的delete()方法即可:

1 >>> p = Publisher.objects.get(name="O‘Reilly")2 >>> p.delete()3 >>> Publisher.objects.all()4 [<Publisher: Apress Publishing>]

  同样我们可以在结果集上调用delete()方法同时删除多条记录。这一点与我们上一小节提到的update()方法相似:

1 >>> Publisher.objects.filter(country=USA).delete()2 >>> Publisher.objects.all().delete()3 >>> Publisher.objects.all()4 []

  删除数据时要谨慎! 为了预防误删除掉某一个表内的所有数据,Django要求在删除表内所有数据时显示使用all()。 比如,下面的操作将会出错: 

1 >>> Publisher.objects.delete()2 Traceback (most recent call last):3   File "<console>", line 1, in <module>4 AttributeError: Manager object has no attribute delete

  而一旦使用all()方法,所有数据将会被删除:

1 >>> Publisher.objects.all().delete()

 

Python框架之Django学习笔记(十一)