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需要更多的学习,对于实战经验也需要更多的积累。