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

    接着上篇。

    10-一次更新多个对象

    有时想要对QuerySet中的所有对象的某一个field来设定一个值,这时候可以像下边这样用update():

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

    这样使用的update()只能是没有关联关系的model或者有ForeignKey的model。如果是有ForeignKey的话,像下边这样用:

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

    update()会立即生效并返回被query匹配的行的个数。但是如果有的行已经有了新的值,则可能与update()返回的个数不相等。QuerySet的update()只有一个限制条件,就是只能作用于一个数据表(database table),也就是model的主table。可以根据相关联的field来过滤一个query,但是只能update model的主表中的columns。例如:

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

    要知道update()方法直接会转换为SQL语句,也可以做批量的update。不会进行任何save()操作的,也不会发出pre_save()或者post_save()的信号(这个信号是调用save()方法的顺序),也不会用auto_now这个field选项的。如果想要对QuerySet的每一个实例都使用save()方法的话,只要遍历这个QuerySet,用for循环就行:

    for item in my_queryset:
        item.save()

    用update()方法时,也可以用F()对象来进行更新,根据此model中的另一个field值。尤其是当增加技术的时候很好用,下例就是一个增加pingback计数的用法:

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

    然而,与在filter中的F()对象不同的是,不能对一个在update中的F()使用joins功能,只能引用要更新的model中的field,如果师徒对一个F()对象使用jions,会有FieldError错误:

    # THIS WILL RAISE A FieldError
    >>> Entry.objects.update(headline=F('blog__name'))

    11-相关的对象

    在一个model中定义了一个关系时(例如,ForeignKey, OneToOneField, ManyToManyField),这个model的实例会有很方便的API来接触相关对象。像前边讲到的例子,一个Entry的对象e,可以e.blog来连接Blog的对象blog。背后其实用到的是python的语法。

    对于这些关联关系的另一面来说,django也会有一些其他的API。例如,Blog的对象blog可以连接到一系列的Entry的对象,通过方法entry_set,像这样:b.entry_set.all()。

    下边的所有例子都是用到了前文提到的Blog, Author, Entry。

    1)-一对多关系

    如果一个model有一个ForeignKey, 这个model的实例就会有连接他的foreign关系的对象的接口:

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

    上边代码,Entry中有一个ForeignKey指向Blog,所以,blog可以看作是Entry的一个成员,类似于对象的成员这样调用。

    当然,只有save()的时候才能保存,这个是一直在强调的:

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

    如果ForeignKey的field中有一个参数null=True,那么可以分配个None值来解除这个Foreign关系:

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

    当第一次接触到一个对象的database的时候,取到的值会被缓存下来,接下来如果还用到的话,就不用去接触database了,而是直接从缓存中取数据,这是在one-to-many这样的关系时的情况,记得前边讲到过,其他情况下,这样是不会产生缓存的,还是会第二次hit the database:

    >>> 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.

    2)-把这种关系反过来

    Entry中有一个ForeignKey关系到Blog,上边的例子是e.blog,那如果已知b是Blog的对象,对应的b.entry会是什么呢?实际上,是会返回所有的entry的对象的一个QuerySet,这个方法市一中Manager,但要明确并不是b.entry这样写,而是下面例子中的F00_set,其中F00是含有ForeignKey的model的实例(例如entry),注意是小写的entry:

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

    当然这个entry_set是可以重写的,如果定义了

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

    除了在前文中提到的对象检索中的QuerySet的方法,ForeignKeyManager还有额外的方法来处理相关联的对象。完备的资料参考related objects reference

    add(obj1, obj2, ...)

    把指定的model的对象加到相关联的model的对象中。(应该是可以把entry加到blog中)

    create(**kwargs)

    创建一个新的对象,保存并把放进相关联的对象中。返回新创建的对象。

    clear()

    把相关联的对象中的所有对象都移除。

    对相关联的对象一次添加多个对象,可以像下面这样写:

    b = Blog.objects.get(id=1)
    b.entry_set = [e1, e2]

    e1和e2可以使完整的Entry的实例,也可以是整数型的pirmary key的值。

    如果有clear()方法,那么在entry_set添加新的对象之前,之前的那些对象都会被移除,如果没有clear()方法,那么添加entry_set不回移除之前的对象。

    这一段讨论的检索方法,都会立即生效于database,每个例子中的添加和删除都会自动save,所以不用再调用save方法了。

    3)-Many-to-Many关联

    这种关联的两头的对象都有自动的API,来连接另一端,也就是可以理解为关联的两头是地位相等的,对称的。这个API就像上面提到的one-to-many时反过来的那个  ”多到一“  的那样操作。唯一的区别是参数的名字上:定义了ManyToManyField的model用自己的field的名字,关联另一端的那个model使用定义关联的model的名字,加上_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.

    当然,Entry中定义了ManyToManyField,Author是此关联的另一端,所以a.entry_set.all()。

    就像ForeignKey, ManyToManyField可以定义一个related_name,上边的例子中也可以定义Entry中的related_name='entries',则entry_set就可以用entries代替了。

    4)-One-to-one关联

    这种关联与many-to-many关联类似,如果在一个model中定义了OneToOneField,这个model就可以用简单的方法来连接到相关联的那个model:

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

    不同的“反过来”的query,one-to-one关联中的被关联的model也有一种Manager,只不过这个Manager返回的只是一个对象,而不是一些对象,很好理解,因为one-to-one关系嘛:

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

    如果关联的model没有对象的话,会有错误DoesNotExist。

    这种“反过来”也可以给对象赋值为一个对象,就像顺序关联的时候那样:

    e.entrydetail = ed

    5)-为什么“反过来”的关联是可能的呢?

    别的对象关联图要求必须在关联的两端都要定义这种关联关系。django开发者认为i这是一种对DRY(Don't Repeat Yourself)准则的践踏(- -!),所以django只要求在一端来定义关联关系就可以了。

    想像一下,凭感觉来讲,在的django中,貌似是直到别的model class被加载的时候,这个定义了关联关系的model class才知道自己关联到了哪里,也就是说,其实之前定义了关联的model class是不知道自己要被关联到哪里的,按道理说这怎么可能实现呢?

    其实答案就在settings.py文件中INSTALLED_APPS中,当每个model被第一次加载的时候,django会在INSTALLED_APPS中列出来这些model,然后在内存中记录下“反过来”的关联,来备用。基本的,INSTALLED_APPS的一个作用就是告诉django整个model的控制器。

    6)-相关联的对象的查询

    包含相关联的对象的查询与普通的查询是一样的,有可以用对象的实例来作为filter条件,也可以用primary key。例如,Blog的对象是blog,其id=5,则如下:

    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

    最后-不妨看看原生的SQL

    如果发现写一个SQL的query来给django来用太麻烦,不妨自己手写一个SQL代码段,django也提供了一些选项可以写原生的SQL语句,可以参考 Performing raw SQL queries.

    特别要注意的是,django的databse的表层(layer)只不过仅仅是对于你的database的一个接口,一个界面(interface),除了django当然也可以用其他的方法来处理database。

    MakingQuery到此结束,感觉囫囵吞枣的看来一遍,还有很多地方只能是猜到大概的意思,理解不深,对于SQL需要更多的学习,对于实战经验也需要更多的积累。

  • 相关阅读:
    织梦开发——相关阅读likeart应用
    织梦标签教程
    织梦专题调用代码
    HIT 2543 Stone IV
    POJ 3680 Intervals
    HIT 2739 The Chinese Postman Problem
    POJ 1273 Drainage Ditches
    POJ 2455 Secret Milking Machine
    SPOJ 371 Boxes
    HIT 2715 Matrix3
  • 原文地址:https://www.cnblogs.com/ee2213/p/3925624.html
Copyright © 2011-2022 走看看