zoukankan      html  css  js  c++  java
  • django python query Making queries 模型的数据库查询 good

    Making queries -- 模型的数据库查询

    参考

    1. http://docs.djangoproject.com/en/dev/topics/db/queries/#topics-db-queries

    Making queries

    一当创建好一个数据模型 (data models),Django 默认提供了一套数据库抽象的 API,可以 create,retrieve,update 和 delete 对象。本文描述如何使用这些 API。

    下面定义的 models 是我们的示例中要使用的:

    class Blog(models.Model):
        name = models.CharField(max_length=100)
        tagline = models.TextField()
    
        def __unicode__(self):
            return self.name
    
    class Author(models.Model):
        name = models.CharField(max_length=50)
        email = models.EmailField()
    
        def __unicode__(self):
            return self.name
    
    class Entry(models.Model):
        blog = models.ForeignKey(Blog)
        headline = models.CharField(max_length=255)
        body_text = models.TextField()
        pub_date = models.DateTimeField()
        authors = models.ManyToManyField(Author)
        n_comments = models.IntegerField()
        n_pingbacks = models.IntegerField()
        rating = models.IntegerField()
    
        def __unicode__(self):
            return self.headline
    

    Creating objects

    Django 使用一种非常直观的方式来表现数据库表:一个 model class 表示一个 数据库表,一个 class 的实例表示数据库表中的一条记录。

    创建一个 object , 使用参数实例化一个 model class ,然后调用 save() 方法 就可以将数据保存到数据库里。

    你可以从任何 Python 能找到的地方 import 一个 model class :

    >>> from mysite.blog.models import Blog
    >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
    >>> b.save()
    

    上面的实例化和save()相当于执行了一条 INSERT SQL 语句。只有你明确地调用 save() 方法,不如 Django 不会动你的数据库。 save() 没有返回数据。

    可以调用 create() ,一次性执行创建实例和 save() 的过程。

    Saving changes to objects

    将改变保存到数据库也用 save() 方法。例如:

    >> b5.name = 'New name'
    >> b5.save()
    

    上面的相当于执行一条 UPDATE SQL 语句。

    Saving ForeignKey and ManyToManyField fields

    保存 ForeignKey field 和保存其他数据完全一致:

    >>> cheese_blog = Blog.objects.get(name="Cheddar Talk")
    >>> entry.blog = cheese_blog
    >>> entry.save()
    

    上面的 cheese_blog 是一个 Blog 实例对象, entry 是一个 Entry 实例对象。 在开始部分的 model 定义里, Entry 有一个 blog 属性,是一个 ForeignKey 。这里直接把 cheese_blog 赋给 entry.blog 了,后面会看到还有其他方式。

    更新一个 ManyToManyField 有点不同,要使用 add() 方法:

    >> joe = Author.objects.create(name="Joe")
    >> entry.authors.add(joe)
    

    Retrieving objects

    要想从数据库里查询数据,需要用 Manager 在 model class 上创建一个 QuerySet。

    QuerySet 是从数据库得到一个对象集合,可以是零、壹或者多个 filters 。在 SQL 语法里, QuerySet 等价于一条 SELECT 语句, 一个 filter 类似于 WHERE 或者 LIMIT 。

    我们是通过 model 的 Manager 获取 QuerySet 的,每个 model 至少需要一个 Manager ,默认的名字是 "objects" (我们可以像定义其他 Class 属性一样, 将一个 Manager 赋值给 objects) :

    >>> Blog.objects
    <django.db.models.manager.Manager object at ...>
    >>> b = Blog(name='Foo', tagline='Bar')
    >>> b.objects
    Traceback:
        ...
    AttributeError: "Manager isn't accessible via Blog instances."
    

    Retrieving all objects

    得到一个数据库表的所有记录对象很简单:

    >>> all_entries = Entry.objects.all()
    

    Retrieving specific objects with filters

    Manager 提供的 root QuerySet 描述了数据库表里的所有 objects。但更多时候 我们想知道某些特定的数据。我们需要对这个 root QuerySet 做进一步工作。

    最常用的两种方式:

    filter(**kwargs)
    返回一个符合指定条件的新的 QuerySet
    exclude(**kwargs)
    返回不符合指定条件的新的 QuerySet

    例如,获取 2006 年的 entries :

    Entry.objects.filter(pub_date__year=2006)
    

    此处我们不需要使用 all() ,虽然 Entry.objects.all().filter(...) 也能工 作。只有在想取得所有 objects 时候,需要 all()

    CHAINING FILTERS (连接多个 filter)

    查询的结果也是 QuerySet ,所以我们可以很容易连接多个查询:

    >>> Entry.objects.filter(
    ...     headline__startswith='What'
    ... ).exclude(
    ...     pub_date__gte=datetime.now()
    ... ).filter(
    ...     pub_date__gte=datetime(2005, 1, 1)
    ... )
    

    FILTERED QUERYSETS ARE UNIQUE

    如果想得到每次查询的结果,可以保存起来,这样可以作为下次查询的 QuerySet:

    >> q1 = Entry.objects.filter(headline__startswith="What")
    >> q2 = q1.exclude(pub_date__gte=datetime.now())
    >> q3 = q1.filter(pub_date__gte=datetime.now())
    

    QUERYSETS ARE LAZY

    OTHER QUERYSET METHODS

    大多数情况下只需要 all(),filter() 和 exclude() ,如果想知道更多的 QuerySet 方法,参考: QuerySet API

    Limiting QuerySets

    可以使用 Python 的 list 切片分割 QuerySet :

    >>> Entry.objects.all()[:5]
    >>> Entry.objects.all()[5:10]
    >>> Entry.objects.all()[:10:2]
    >>> Entry.objects.order_by('headline')[0]
    >>> Entry.objects.order_by('headline')[0:1].get()
    

    Field lookups

    最常见的 lookups 是 "field__lookuptype=value" :

    >>> Entry.objects.filter(pub_date__lte='2006-01-01')
    
    

    相当于下面 SQL 语句 :

    SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
    

    exact

    >>> Entry.objects.get(headline__exact="Man bites dog")
    

    相当于:

    SELECT ... WHERE headline = 'Man bites dog';
    

    默认使用 exact ,下面两条语句等价:

    >>> Blog.objects.get(id__exact=14)  # Explicit form
    >>> Blog.objects.get(id=14)         # __exact is implied
    

    iexact

    忽略大小写的绝对查询:

    >>> Blog.objects.get(name__iexact="beatles blog")
    

    对于 "Beatles Blog", "beatles blog", "BeAtlES blOG" 等都能匹配。

    contains / icontains

    contains 大小写敏感的相似查询:

    Entry.objects.get(headline__contains='Lennon')
    

    相当于 SQL 语句:

    SELECT ... WHERE headline LIKE '%Lennon%';
    

    startswith / endswith

    Lookups that span relationships

    扩展关系查询。例如,查询所有 Entry 对象里面 Blog 属性名字为 'Beatles Blog' :

    >>> Entry.objects.filter(blog__name__exact='Beatles Blog')
    

    对于所有 Blog 对象里,至少有一个 Entry 属性的 headline 包含 'Lennon' 查 询:

    >>> Blog.objects.filter(entry__headline__contains='Lennon')
    

    这种关系查询方式下,如果对应的对象没有某个属性,也并不和其他查询一样返 回错误。

    Blog.objects.filter(entry__author__name='Lennon')
    

    Spanning multi-valued relationships

    如果想 filtering 一个 ManyToManyField 或反向 ForeignKeyField, 有两种不 同方法。

    思考示例里的 Blog/Entry 关系(Blog 到 Entry 是 one-to-many 关系)。思考 下面示例:

    Blog.objects.filter(entry__headline__contains='Lennon',
            entry__pub_date__year=2008)
    Blog.objects.filter(entry__headline__contains='Lennon').filter(
            entry__pub_date__year=2008)
    

    Filters can reference fields on the model

    要想把要查询的数据和 model 自己的字段对比,即使用 model 自己字段作为一 个查询量,可以使用 F 方法:

    >>> from django.db.models import F
    >>> Entry.objects.filter(n_pingbacks__lt=F('n_comments'))
    >>> Entry.objects.filter(n_pingbacks__lt=F('n_comments') * 2)
    >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
    
    

    这里 n_pingbacks 和 n_comments 都时 Entry model 的属性。

    甚至可以在关系中使用:

    >>> Entry.objects.filter(author__name=F('blog__name'))
    

    The pk lookup shortcut

    pk 表示 "primary key" , Django 默认为每个 model 创建一个 id 属性(自增 的 primary key),可以使用 pk 代替 id 操作。

    >>> Blog.objects.get(id__exact=14) # Explicit form
    >>> Blog.objects.get(id=14) # __exact is implied
    >>> Blog.objects.get(pk=14) # pk implies id__exact
    

    pk 不仅可以和 exact 何用,还可以:

    # Get blogs entries with id 1, 4 and 7
    >>> Blog.objects.filter(pk__in=[1,4,7])
    
    # Get all blog entries with id > 14
    >>> Blog.objects.filter(pk__gt=14)
    

    在关系连接中也可以使用:

    >>> Entry.objects.filter(blog__id__exact=3) # Explicit form
    >>> Entry.objects.filter(blog__id=3)        # __exact is implied
    >>> Entry.objects.filter(blog__pk=3)        # __pk implies __id__exact
    

    Escaping percent signs and underscores in LIKE statements

    使用 SQL 的 LIKE 语句查询的 field lookups (iexact, contains, icontains, startswith, istartswith, endswith and iendswith) 会自动替换 两个特殊字符: %(百分号) 和 (下划线) 。在 LIKE 语句中百分号是元字符, 下划线也是。

    对于这样的 Django 查询:

    >>> Entry.objects.filter(headline_contains='%')
    

    对应这样语句:

    SELECT ... WHERE headline LIKE '%\%%';
    

    Caching and QuerySets

    Complex lookups with Q objects

    在 filter 里面的查询关系都是 'AND' 逻辑。如果想表达复杂的查询,请使用 Q 方法(django.db.models.Q)。 Q 方法会封装一些查询,简单封装示例如下:

    Q(question__startswith='What')
    

    Q 方法可以使用 "&" 和 "|" 符号。

    Q(question__startswith='Who') | Q(question__startswith='What')
    

    相当于:

    WHERE question LIKE 'Who%' OR question LIKE 'What%'
    

    还可以使用 "~" 表示 "NOT" :

    Q(question__startswith='Who') | ~Q(pub_date__year=2005)
    

    每个 lookup 函数 (如 filter(), exclude(), get())都可以使用一个和多个 Q 对象作为查询条件:

    Poll.objects.get(
        Q(question__startswith='Who'),
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    )
    

    相当于 SQL :

    SELECT * from polls WHERE question LIKE 'Who%'
        AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
    

    Lookup 函数也可以混合使用 Q 对象和关键字查询:

    Poll.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        question__startswith='Who')
    

    都是下面用法无效:

    # INVALID QUERY
    Poll.objects.get(
        question__startswith='Who',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
    

    Comparing objects

    同 Python 其他对象一样:

    >>> some_entry == other_entry
    >>> some_entry.id == other_entry.id
    >>> some_obj == other_obj
    >>> some_obj.name == other_obj.name
    

    Deleting objects

    删除一个对象很简单:

    e.delete()
    

    每个 QuerySet 都有 delete() 方法,可以批量删除:

    Entry.objects.filter(pub_date__year=2005).delete()
    

    Updating multiple objects at once

    # Update all the headlines with pub_date in 2007.
    Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
    

    update() 方法只能在没有外联( non-relation )或者 ForeignKey fields 上使用:

    >>> b = Blog.objects.get(pk=1)
    
    # Change every Entry so that it belongs to this Blog.
    >>> Entry.objects.all().update(blog=b)
    

    update() 会立即应用并返回修改的对象个数。唯一的限制是,必须作用于 main table :

    >>> b = Blog.objects.get(pk=1)
    
    # Update all the headlines belonging to this Blog.
    >>> Entry.objects.select_related().filter(blog=b).update(headline='Everything is the same')
    

    也可以使用 F() :

    >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
    

    但是不能使用关联的属性:

    # 下面语句会引发 FieldError 异常
    >>> Entry.objects.update(headline=F('blog__name'))
    

    Related objects

    如果在 model 里定义了 relationship (比如:ForeignKey, OneToOneField, or ManyToManyField)。这个 model 的示例也有取得其关系对象的方法。

    再次重申,下面所有示例使用本文开头定义的 model 。

    Blog 对象 b 要访问关联的 Entry 对象,可以通过 entry_set 属性:b.entry_set.all()

    One-to-many relationships

    FORWARD (正向访问)

    如果一个 model 有一个 ForeignKey ,通过它的实例就可以访问这个关联对象:

    >>> e = Entry.objects.get(id=2)
    >>> e.blog # Returns the related Blog object.
    

    可以访问和设置这个 foreignkey 属性:

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = some_blog
    >>> e.save()
    

    如果 Foreignkey 的定义有 "null=True" 设置,可以把 "None" 赋给它:

    >>> e = Entry.objects.get(id=2)
    >>> e.blog = None
    >>> e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
    

    对于 cache:

    >>> e = Entry.objects.get(id=2)
    >>> print e.blog  # Hits the database to retrieve the associated Blog.
    >>> print e.blog  # Doesn't hit the database; uses cached version.
    
    >>> e = Entry.objects.select_related().get(id=2)
    >>> print e.blog  # Doesn't hit the database; uses cached version.
    >>> print e.blog  # Doesn't hit the database; uses cached version.
    

    FOLLOWING RELATIONSHIPS "BACKWARD" (反向)

    如果一个 model A 有 Foreignkey, 那么 A 的 Foreignkey 指向的 model B 的 实例便有一个 Manager 方法。这个 Manager 方法可以返回所有关联的 model A 的实例。

    默认情况下这个 Manager 名称形式为: "FOO_set" 。 "FOO" 是 model A 的 Foreignkey field 属性名。 这个 Manager 返回的也是 QuerySet 形式。

    >>> b = Blog.objects.get(id=1)
    >>> b.entry_set.all() # Returns all Entry objects related to Blog.
    
    # b.entry_set is a Manager that returns QuerySets.
    >>> b.entry_set.filter(headline__contains='Lennon')
    >>> b.entry_set.count()
    

    注意: "FOO_set" 字符串也可以替换为 Foreignkey field 定义时用 related_name 指定的字符串。比如我们的 Entry model 里有这样的定义:"blog = Foreignkey(Blog, related_name='entries'), 我们就可以用下面形式:

    >>> b = Blog.objects.get(id=1)
    >>> b.entries.all() # Returns all Entry objects related to Blog.
    
    # b.entries is a Manager that returns QuerySets.
    >>> b.entries.filter(headline__contains='Lennon')
    >>> b.entries.count()
    

    ForeignKey 的 QuerySet 和上面介绍的 QuerySet 比,还有下面方法:

    add(obj1,obj2,...)
    增加指定的 model objects 到 related object set。
    create(kwargs)
    创建行 object ,保存并把它放到 related object set 里。返回新创建的 object。
    remove(obj1,obj2,...)
    从 related object set 里删除
    clear()
    清楚所有 related object set

    还可以这样:

    b = Blog.objects.get(id=1)
    b.entry_set = [e1, e2]
    
    Many-to-many relationships

    和 one-to-many relationship 的 "backward" 方法一致,唯一区别就是属性的 名字: 定义 ManyToManyField 的 model 使用 field 自身的名字访问,而 "reverse" model (reverse model 指 ManyToManyField 里指向的 model) 使用 小写 model name 加上 'set' 。

    e = Entry.objects.get(id=3)
    e.authors.all() # Returns all Author objects for this Entry.
    e.authors.count()
    e.authors.filter(name_contains='John')
    
    a = Author.objects.get(id=5)
    a.entry_set.all() # Returns all Entry objects for this Author.
    

    ManyToManyField 也可以使用 related_name 指定的字符串,假如 Entry 的定义 "authors = models.ManyToManyField(Author)" 修改为 'authors = models.ManyToManyField(Author,related_name="entries")' ,那么 Author 实 例就可以用 'entries' 代替 'entry_set' 来访问了。

    One-to-One relationships

    和 many-to-one 很相似 :

    class EntryDetail(models.Model):
        entry = models.OneToOneField(Entry)
        details = models.TextField()
    
    ed = EntryDetail.objects.get(id=2)
    ed.entry # Returns the related Entry object.
    

    不同的地方是 "reverse" 查询:

    e = Entry.objects.get(id=2)
    e.entrydetail # returns the related EntryDetail object
    

    How are the backward relationships possible?

    Queries over related objects

    Entry.objects.filter(blog=b) # Query using object instance
    Entry.objects.filter(blog=b.id) # Query using id from instance
    Entry.objects.filter(blog=5) # Query using id directly
    

    Falling back to raw SQL

    想使用 raw SQL : Performing raw SQL queries

  • 相关阅读:
    A CIRCULAR PROGRESSBAR STYLE USING AN ATTACHED VIEWMODEL
    First MarkDown Blog
    Folder Recursion with C#
    39. Volume Rendering Techniques
    Service Station
    WPF Wonders: Transformations (and Robots!)
    Flipping elements with WPF
    FoLlow 的技术博客
    利用索引降低并发事务引起的锁【转】
    企业级应用架构模型-并发,事务,锁
  • 原文地址:https://www.cnblogs.com/lexus/p/2812957.html
Copyright © 2011-2022 走看看