zoukankan      html  css  js  c++  java
  • django学习之Model(四)MakingQuery

    上一篇写到MakingQuey中的filter,本篇接着来。

     10)-扩展多值的关系

    如果对一个ManyToManyField或ForeignKey的表进行filter过滤查询的话,有2中方法可以用。分别是:

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

    假设,现有一个blog表,它对应了很多的entry的表,而且这个blog既有含有“Lennon”的entry,也有含有2008的entry,但是没有那种2者都有的entry,那么第一个方法过滤的是2者都有的entry,所以没有符合条件的blog返回。第一种方法可以理解为“并且”的意思。第二种是第一个filter返回含有“Lennon”的blog,然后在这个blog集合中,再选有2008的,所以会有blog返回。开始理解起来很费劲,抓住特点:一个blog对应多个entry。例如,blog1对应了entry1,entry2,entry3,其中entry1只有“Lennon”,没有2008;entry2只有2008,没有“Lennon”;entry3则什么都没有。那么用第一种方法来filter的时候,则没有满足既有Lennon又有2008的entry;第二种方法的第一个filter,因为entry1有Lennon,所以blog1为返回值,然后对返回值blog1进行filter,条件为有2008,而blog1中的entry2是有2008的,所以blog1作为返回值。

    这个也适用于exlude().

    11)-filters可以引用model中的fields

    class F

    目前为止的例子中,都是用field中的值来作为filter的条件来进行数据查询。但是如果需要把一个field的值与他所在的model的其他field的值呢?

    django提供F()表达式来实现这种比较。F()的实例是在一个query中来对model中的field进行引用。这些引用可以作为filter的条件来进行过滤。

    例如,要查询blog的entry,comments满足的数量比pingbacks的多,可以用F()来作用在pingbacks上,然后把结果作为filter的条件:

    >>> from django.db.models import F
    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))

    注意:F()表达式中是n_comments比大n_pingbacks。所以,也有倍数、加、减这样的操作可以来比较:

    >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)#
    n_comments比n_pingbacks的2倍多

    >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))#n_comments与n_pingbacks的和比rating多

    gt是多理解为>,lt理解为<. 

    而下面的例子则是两个字符串一样:

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

    12)-pk

    是primary key的意思:

    >>> 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
    # 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)

    同样应该有这些符号的组合,分别是__双下划线,lt,gt,exact等等。

    如果查询含有%符号的,下例给出:

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

    同样的_下划线符号也是,django会自动来处理的,就像上边这行程序这样写就行了。

    5-QuerySet的缓存

    每个QuerySet都有缓存来降低对数据库database的访问。弄明白了之后,可以写出高效率的代码。

    在新创建的QuerySet中,缓存是空的。当QuerySet第一次 被计算的时候,django把QuerySet放在其缓存中,然后把具体要查询的数据项作为结果返回。把QuerySet这种缓存的行为时刻记住,有的时候很容易使用不当的。例如下面,建立了2个QuerySet,分别使用后,就丢掉了,这样造成了2次访问database,而且第二个e很可能不是第一个e,因为在第二个发生之前,database很可能已经被写入新数据或删除什么东西了:

    >>> print([e.headline for e in Entry.objects.all()])
    >>> print([e.pub_date for e in Entry.objects.all()])

    正确的做法如下,建立个变量来存放QuerySet,让它一直放在内存中,然后每次用的时候从内存中去读取:

    >>> queryset = Entry.objects.all()
    >>> print([p.headline for p in queryset]) # Evaluate the query set.
    >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

    注意,第一行并没有touch the database,访问数据库发生在第二行代码。

    QuerySet也并不是总缓存结果的,当计算queryset的一部分的时候,要缓存,但是如果只是用一个下一层的queryset来访问结果的时候,并没有缓存。这也意味着,可以用数组分片或者一个索引来限制queryset而不产生缓存,例如下面的例子,显示只定义了一个queryset来存放objects,然后用数组来访问其中的某一个,这样就没有缓存,每次都要重新访问数据库:

    >>> queryset = Entry.objects.all()
    >>> print queryset[5] # Queries the database
    >>> print queryset[5] # Queries the database again

    然而,如果整个queryset都被计算了,那么就会产生缓存:

    >>> queryset = Entry.objects.all()
    >>> [entry for entry in queryset] # Queries the database
    >>> print queryset[5] # Uses cache
    >>> print queryset[5] # Uses cache

    还有一些例子也是整个querset都被计算了,当然产生了缓存:

    >>> [entry for entry in queryset]
    >>> bool(queryset)
    >>> entry in queryset
    >>> list(queryset)

    6-用Q对象来进行复杂的查询

    在filter()方法中,用关键字来查询,这些关键字是AND的关系,即”并且“,如果需要进行更复杂的查询,例如OR关系,可以用Q对象这种方法。

    Q对象(django.db.models.Q)是用来封装关键字的集合的。这些关键字包括上文的lookup查询中所提到的关键字。例如,下面这个Q对象封装了LIKE查询:

    from django.db.models import Q
    Q(question__startswith='What')

    Q对象可以用逻辑符号&或者|来连接,结果返回一个Q对象:

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

    同等于SQL语句:

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

    可以把Q对象组合成复杂的表达,也可以用~符号,代表”NOT,非“的意思:

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

    每个查询函数(filter(), get(), exclude())都需要有关键字,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')

    查询函数也可以混合使用keywords和Q object,但是要注意,Q object必须在keyword之前定义:

    #正确的
    Poll.objects.get(
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
        question__startswith='Who')
    #错误的
    Poll.objects.get(
        question__startswith='Who',
        Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))

     7-比较对象

    用python标准的比较操作符==就可以进行model实例的比较。而这实际上实在比较primarykey的值:

    >>> some_entry == other_entry
    >>> some_entry.id == other_entry.id#实际上

    如果model的primarykey不叫id,没关系,照样还是在比较primarykey,例如一个model的primarykey是name,则下面2行代码是同样意思的:

    >>> some_obj == other_obj
    >>> some_obj.name == other_obj.name

    8-删除对象

    delete()用来进行对象的删除,而且是立即生效,并且没有返回值:

    e.delete()

    也可以批量来进行删除操作,每个QuerySet都有delete()操作,可以把QuerySet中的所有对象都删掉:

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

    delete是Manager的一个方法,但是由于怕用户误操作而没有显式的显现出来。因为delete是纯粹的在SQL中执行的方法,所以不需要用户在过程中去认为的调用。如果你在一个model的class定义中自己定义了一个delete()的话,那么就相当于覆盖掉了Manager提供的delete,这样就需要用户自己去调用了,而且这种自己定义的delete()还不能批量的来操作,也就是不能作为QuerySet中的参数来调用,而是需要自己对每个object单独进行delete一次。

    django提供的delete()方法,会删除由foreignkey来关联到一起的object:

    b = Blog.objects.get(pk=1)
    # This will delete the Blog and all of its Entry objects.
    b.delete()

    这种串联的用法是通过 on_delete 参数传递给Foreignkey的。

    delete是Manager的一个方法,为了防止用户误操作,所以不用显式调用,但是如果确实需要调用delete()来删除所有的objects的话,应该明确的指出来这个QuerySet:

    Entry.objects.all().delete()

    9-复制model实例

    尽管没有built-in的方法来实现model 实例的复制,但是可以用model的所有的fields的值来创建一个新的实例,最简单的情况就是,先把pk设置成None,然后再save:

    1 blog = Blog(name='My blog', tagline='Blogging is easy')
    2 blog.save() # blog.pk == 1
    3 
    4 blog.pk = None
    5 blog.save() # blog.pk == 2

    思考:也就是,第1行代码只是把Blog内容放在了blog这个变量中,并没写进去数据库,只有调用save()方法时,才发生写的操作。而且每当调用save(),都相当于要把这个数据(blog)写进数据库。而数据库的pk是唯一而不能重复的,所有先要把blog.pk设成None,然后再操作,由SQL语句把pk由依次递增为2了。

    如果有model的继承时,会略复杂一些:

    class ThemeBlog(Blog):
        theme = models.CharField(max_length=200)
    
    django_blog = ThemeBlog(name='Django', tagline='Django is easy', theme='python')
    django_blog.save() # django_blog.pk == 3

    因为继承的工作原理,这次需要把pk和id都设成None了:

    django_blog.pk = None
    django_blog.id = None
    django_blog.save() # django_blog.pk == 4

    这个过程并没有复制相关联的objects,如果想要复制这种关系,需要多写一些代码了。在本文的例子中,Entry和Author是多对多的关系(ManyToManyField):

    1 entry = Entry.objects.all()[0] # some previous entry
    2 old_authors = entry.authors.all()
    3 entry.pk = None
    4 entry.save()
    5 entry.authors = old_authors # saves new many2many relations

    今天到这,明天继续!

  • 相关阅读:
    LeetCode(111) Minimum Depth of Binary Tree
    LeetCode(108) Convert Sorted Array to Binary Search Tree
    LeetCode(106) Construct Binary Tree from Inorder and Postorder Traversal
    LeetCode(105) Construct Binary Tree from Preorder and Inorder Traversal
    LeetCode(99) Recover Binary Search Tree
    【Android】通过经纬度查询城市信息
    【Android】自定义View
    【OpenStack Cinder】Cinder安装时遇到的一些坑
    【积淀】半夜突然有点想法
    【Android】 HttpClient 发送REST请求
  • 原文地址:https://www.cnblogs.com/ee2213/p/3919669.html
Copyright © 2011-2022 走看看