Fork me on GitHub

新博客地址:voidy's blog

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

Posted on 2014-09-28 09:17  尛鱼  阅读(1311)  评论(0编辑  收藏  举报

  话说上次说到数据库的基本访问,而数据库我们主要进行的操作就是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.id
2 3L    # this will differ based on your own data 

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

1 >>> p.name = 'Apress Publishing'
2 >>> p.save()

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

1 UPDATE books_publisher SET
2     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.com'
8 WHERE id = 3L;

 

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

  选择对象

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

1 >>> Publisher.objects.all()
2 [<Publisher: Apress>, <Publisher: O'Reilly>]

  这相当于这个SQL语句:

1 SELECT id, name, address, city, state_province, country, website
2 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, website
2 FROM books_publisher
3 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, website
2 FROM books_publisher
3 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, website
2 FROM books_publisher
3 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: O'Reilly>]

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

1 SELECT id, name, address, city, state_province, country, website
2 FROM books_publisher
3 ORDER BY name;

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

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

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

1 >>> Publisher.objects.order_by("-name")
2 [<Publisher: O'Reilly>, <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.name
11 
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 Publishing'
3 >>> p.save()

  这等同于如下SQL语句:

SELECT id, name, address, city, state_province, country, website
FROM books_publisher
WHERE 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.com'
WHERE id = 3L;

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

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

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

1 UPDATE books_publisher
2 SET name = 'Apress Publishing'
3 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()