• django模型——数据库(二)


    模型——数据库(二)

    实验简介

    模型的一些基本操作,save方法用于把对象写入到数据库,objects是模型的管理器,可以使用它的delete、filter、all、order_by和update等函数。

    一、基本数据访问

    一旦你创建了模型,Django自动为这些模型提供了高级的Python API。 运行python manage.py shell并输入下面的内容试试看:

    >>> from books.models import Publisher
    >>> p1 = Publisher(name='Apress', address='2855 Telegraph Avenue',
    ...     city='Berkeley', state_province='CA', country='U.S.A.',
    ...     website='http://www.apress.com/')
    >>> p1.save()
    >>> p2 = Publisher(name="O'Reilly", address='10 Fawcett St.',
    ...     city='Cambridge', state_province='MA', country='U.S.A.',
    ...     website='http://www.oreilly.com/')
    >>> p2.save()
    >>> publisher_list = Publisher.objects.all()
    >>> publisher_list
    [<Publisher: Publisher object>, <Publisher: Publisher object>]
    

    这短短几行代码干了不少的事。 这里简单的说一下:

    1. 首先,导入Publisher模型类, 通过这个类我们可以与包含 出版社 的数据表进行交互。

    2. 接着,创建一个Publisher类的实例并设置了字段name, address等的值。 调用该对象的save()方法,将对象保存到数据库中。 Django 会在后台执行一条 INSERT 语句。

    3. 最后,使用Publisher.objects属性从数据库取出出版商的信息,这个属性可以认为是包含出版商的记录集。 这个属性有许多方法, 这里先介绍调用Publisher.objects.all()方法获取数据库中Publisher类的所有对象。这个操作的幕后,Django执行了一条SQL SELECT语句。

    这里有一个值得注意的地方,在这个例子可能并未清晰地展示。 当你使用Django modle API创建对象时Django并未将对象保存至数据库内,除非你调用save()方法:

    p1 = Publisher(...)
    # At this point, p1 is not saved to the database yet!
    p1.save()
    # Now it is.
    

    如果需要一步完成对象的创建与存储至数据库,就使用objects.create()方法。 下面的例子与之前的例子等价:

    >>> p1 = Publisher.objects.create(name='Apress',
    ...     address='2855 Telegraph Avenue',
    ...     city='Berkeley', state_province='CA', country='U.S.A.',
    ...     website='http://www.apress.com/')
    >>> p2 = Publisher.objects.create(name="O'Reilly",
    ...     address='10 Fawcett St.', city='Cambridge',
    ...     state_province='MA', country='U.S.A.',
    ...     website='http://www.oreilly.com/')
    >>> publisher_list = Publisher.objects.all()
    >>> publisher_list
    

    当然,你肯定想执行更多的Django数据库API试试看,不过,还是让我们先解决一点烦人的小问题。

    添加模块的字符串表现 当我们打印整个publisher列表时,我们没有得到想要的有用信息,无法把对象区分开来:

    >>> publisher_list
    [<Publisher: Publisher object>, <Publisher: Publisher object>]
    

    我们可以简单解决这个问题,只需要为Publisher 对象添加一个方法 unicode() 。 unicode() 方法告诉Python如何将对象以unicode的方式显示出来。 为以上三个模型添加unicode()方法后,就可以看到效果了:

    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()
    
        def __unicode__(self):
            return self.name
    
    class Author(models.Model):
        first_name = models.CharField(max_length=30)
        last_name = models.CharField(max_length=40)
        email = models.EmailField()
    
        def __unicode__(self):
            return u'%s %s' % (self.first_name, self.last_name)
    
    class Book(models.Model):
        title = models.CharField(max_length=100)
        authors = models.ManyToManyField(Author)
        publisher = models.ForeignKey(Publisher)
        publication_date = models.DateField()
    
        def __unicode__(self):
            return self.title
    

    就象你看到的一样, unicode() 方法可以进行任何处理来返回对一个对象的字符串表示。 Publisher和Book对象的__unicode__()方法简单地返回各自的名称和标题,Author对象的__unicode__()方法则稍微复杂一些,它将first_name和last_name字段值以空格连接后再返回。

    对__unicode__()的唯一要求就是它要返回一个unicode对象 如果__unicode__()方法未返回一个Unicode对象,而返回比如说一个整型数字,那么Python将抛出一个TypeError错误,并提示:”coercing to Unicode: need string or buffer, int found” 。

    1. Unicode对象

    什么是Unicode对象呢?

    你可以认为unicode对象就是一个Python字符串,它可以处理上百万不同类别的字符——从古老版本的Latin字符到非Latin字符,再到曲折的引用和艰涩的符号。

    普通的python字符串是经过编码的,意思就是它们使用了某种编码方式(如ASCII,ISO-8859-1或者UTF-8)来编码。 如果你把奇特的字符(其它任何超出标准128个如0-9和A-Z之类的ASCII字符)保存在一个普通的Python字符串里,你一定要跟踪你的字符串是用什么编码的,否则这些奇特的字符可能会在显示或者打印的时候出现乱码。 当你尝试要将用某种编码保存的数据结合到另外一种编码的数据中,或者你想要把它显示在已经假定了某种编码的程序中的时候,问题就会发生。 我们都已经见到过网页和邮件被???弄得乱七八糟。 ?????? 或者其它出现在奇怪位置的字符:这一般来说就是存在编码问题了。

    但是Unicode对象并没有编码。它们使用Unicode,一个一致的,通用的字符编码集。 当你在Python中处理Unicode对象的时候,你可以直接将它们混合使用和互相匹配而不必去考虑编码细节。

    Django 在其内部的各个方面都使用到了 Unicode 对象。 模型 对象中,检索匹配方面的操作使用的是 Unicode 对象,视图 函数之间的交互使用的是 Unicode 对象,模板的渲染也是用的 Unicode 对象。 通常,我们不必担心编码是否正确,后台会处理的很好。

    注意,我们这里只是对Unicode对象进行非常浅显的概述,若要深入了解你可能需要查阅相关的资料。 这是一个很好的起点:http://www.joelonsoftware.com/articles/Unicode.html

    为了让我们的修改生效,先退出Python Shell,然后再次运行 python manage.py shell 进入。(这是保证代码修改生效的最简单方法。)现在Publisher对象列表容易理解多了。

    >>> from books.models import Publisher
    >>> publisher_list = Publisher.objects.all()
    >>> publisher_list
    [<Publisher: Apress>, <Publisher: O'Reilly>]
    

    请确保你的每一个模型里都包含 __unicode__() 方法,这不只是为了交互时方便,也是因为 Django会在其他一些地方用 __unicode__() 来显示对象。

    最后, __unicode__() 也是一个很好的例子来演示我们怎么添加 行为 到模型里。 Django的模型不只是为对象定义了数据库表的结构,还定义了对象的行为。 __unicode__() 就是一个例子来演示模型知道怎么显示它们自己。

    2. 插入和更新数据

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

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

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

    >>> 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() 还多做了一件事: 计算这个主键的值并把它赋值给这个对象实例:

    >>> p.id
    

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

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

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

    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 = 52;
    

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

    UPDATE books_publisher SET
        name = 'Apress Publishing'
    WHERE id=52;
    

    3. 选择对象

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

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

    这相当于这个SQL语句:

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

    注意

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

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

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

    首先,我们有一个已定义的模型 Publisher 。没什么好奇怪的: 你想要查找数据, 你就用模型来获得数据。

    然后,是objects属性。 它被称为管理器,目前,我们只需了解管理器管理着所有针对数据包含、还有最重要的数据查询的表格级操作。

    所有的模型都自动拥有一个 objects 管理器;你可以在想要查找数据时使用它。

    最后,还有 all() 方法。这个方法返回返回数据库中所有的记录。 尽管这个对象 看起来 象一个列表(list),它实际是一个 QuerySet 对象, 这个对象是数据库中一些记录的集合。

    4. 数据过滤

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

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

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

    SELECT id, name, address, city, state_province, country, website
    FROM books_publisher
    WHERE name = 'Apress';
    

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

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

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

    SELECT id, name, address, city, state_province, country, website
    FROM books_publisher
    WHERE country = 'U.S.A.'
    AND state_province = 'CA';
    

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

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

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

    SELECT id, name, address, city, state_province, country, website
    FROM books_publisher
    WHERE name LIKE '%press%';
    

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

    5. 获取单个对象

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

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

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

    >>> Publisher.objects.get(country="U.S.A.")
    Traceback (most recent call last):
        ...
    MultipleObjectsReturned: get() returned more than one Publisher --
        it returned 2! Lookup parameters were {'country': 'U.S.A.'}
    如果查询没有返回结果也会抛出异常:
    
    >>> Publisher.objects.get(name="Penguin")
    Traceback (most recent call last):
        ...
    DoesNotExist: Publisher matching query does not exist.
    

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

    try:
        p = Publisher.objects.get(name='Apress')
    except Publisher.DoesNotExist:
        print "Apress isn't in the database yet."
    else:
        print "Apress is in the database."
    

    6. 数据排序

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

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

    >>> Publisher.objects.order_by("name")
    [<Publisher: Apress>, <Publisher: O'Reilly>]
    

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

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

    我们可以对任意字段进行排序:

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

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

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

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

    >>> Publisher.objects.order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    

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

    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()
    
        def __unicode__(self):
            return self.name
    
        class Meta:
            ordering = ['name']
    

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

    7. 连锁查询

    我们已经知道如何对数据进行过滤和排序。 当然,通常我们需要同时进行过滤和排序查询的操作。 因此,你可以简单地写成这种“链式”的形式:

    >>> Publisher.objects.filter(country="U.S.A.").order_by("-name")
    [<Publisher: O'Reilly>, <Publisher: Apress>]
    

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

    SELECT id, name, address, city, state_province, country, website
    FROM books_publisher
    WHERE country = 'U.S.A'
    ORDER BY name DESC;
    

    8. 限制返回的数据

    另一个常用的需求就是取出固定数目的记录。 想象一下你有成千上万的出版商在你的数据库里, 但是你只想显示第一个。 你可以使用标准的Python列表裁剪语句:

    >>> Publisher.objects.order_by('name')[0]
    <Publisher: Apress>
    

    这相当于:

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

    类似的,你可以用Python的range-slicing语法来取出数据的特定子集:

    >>> Publisher.objects.order_by('name')[0:2]
    

    这个例子返回两个对象,等同于以下的SQL语句:

    SELECT id, name, address, city, state_province, country, website
    FROM books_publisher
    ORDER BY name
    OFFSET 0 LIMIT 2;
    

    注意,不支持Python的负索引(negative slicing):

    >> Publisher.objects.order_by('name')[-1]
    Traceback (most recent call last):
      ...
    AssertionError: Negative indexing is not supported.
    

    虽然不支持负索引,但是我们可以使用其他的方法。 比如,稍微修改 order_by() 语句来实现:

    >> Publisher.objects.order_by('-name')[0]
    

    9. 更新多个对象

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

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

    >>> p = Publisher.objects.get(name='Apress')
    >>> p.name = 'Apress Publishing'
    >>> 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 = 52;
    (注意在这里我们假设Apress的ID为52)
    

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

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

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

    UPDATE books_publisher
    SET name = 'Apress Publishing'
    WHERE id = 52;
    

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

    >>> Publisher.objects.all().update(country='USA')
    

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

    10. 删除对象

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

    >>> p = Publisher.objects.get(name="O'Reilly")
    >>> p.delete()
    >>> Publisher.objects.all()
    [<Publisher: Apress Publishing>]
    

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

    >>> Publisher.objects.filter(country='USA').delete()
    >>> Publisher.objects.all().delete()
    >>> Publisher.objects.all()
    []
    

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

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

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

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

    如果只需要删除部分的数据,就不需要调用all()方法。再看一下之前的例子:

    >>> Publisher.objects.filter(country='USA').delete()
    

    二、小结

    本节讲了模型的一些基本操作,save方法用于把对象写入到数据库,objects是模型的管理器,可以使用它的delete、filter、all、order_by和update等函数。

    作业

    请再插入书和出版社,建立它们之间的联系,最后实现分页查询。

  • 相关阅读:
    golang的并行快速排序
    深信服面试
    一道算法题-勇敢的牛牛
    一道算法题-打车
    一道算法题-最少需要换掉的瓷砖数量
    《C++ Primer Plus》读书笔记之二—复合类型
    图解:TCP协议中的三次握手和四次挥手
    一道算法题-不用加减乘除做加法
    OSI七层模型与TCP/IP五层模型
    Socket编程
  • 原文地址:https://www.cnblogs.com/mrchige/p/6402767.html
Copyright © 2020-2023  润新知