中介模型:
处理类似搭配 pizza 和 topping 这样简单的多对多关系时,使用标准的ManyToManyField 就可以了。但是,有时你可能需要关联数据到两个模型之间的关系上。
例如,有这样一个应用,它记录音乐家所属的音乐小组。我们可以用一个ManyToManyField 表示小组和成员之间的多对多关系。但是,有时你可能想知道更多成员关系的细节,比如成员是何时加入小组的。
对于这些情况,Django 允许你指定一个中介模型来定义多对多关系。 你可以将其他字段放在中介模型里面。源模型的ManyToManyField 字段将使用through 参数指向中介模型。对于上面的音乐小组的例子,代码如下:
from
django.db
import
models
class
Person(models.Model):
name
=
models.CharField(max_length
=
128
)
def
__str__(
self
):
# __unicode__ on Python 2
return
self
.name
class
Group(models.Model):
name
=
models.CharField(max_length
=
128
)
members
=
models.ManyToManyField(Person, through
=
‘Membership‘
)
def
__str__(
self
):
# __unicode__ on Python 2
return
self
.name
class
Membership(models.Model):
person
=
models.ForeignKey(Person)
group
=
models.ForeignKey(Group)
date_joined
=
models.DateField()
invite_reason
=
models.CharField(max_length
=
64
)
既然你已经设置好ManyToManyField 来使用中介模型(在这个例子中就是Membership),接下来你要开始创建多对多关系。你要做的就是创建中介模型的实例:
>>> ringo
=
Person.objects.create(name
=
"Ringo Starr"
)
>>> paul
=
Person.objects.create(name
=
"Paul McCartney"
)
>>> beatles
=
Group.objects.create(name
=
"The Beatles"
)
>>> m1
=
Membership(person
=
ringo, group
=
beatles,
... date_joined
=
date(
1962
,
8
,
16
),
... invite_reason
=
"Needed a new drummer."
)
>>> m1.save()
>>> beatles.members.
all
()
[<Person: Ringo Starr>]
>>> ringo.group_set.
all
()
[<Group: The Beatles>]
>>> m2
=
Membership.objects.create(person
=
paul, group
=
beatles,
... date_joined
=
date(
1960
,
8
,
1
),
... invite_reason
=
"Wanted to form a band."
)
>>> beatles.members.
all
()
[<Person: Ringo Starr>, <Person: Paul McCartney>]
与普通的多对多字段不同,你不能使用add、 create和赋值语句(比如,beatles.members = [...])来创建关系:
# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name
=
"George Harrison"
)
# AND NEITHER WILL THIS
>>> beatles.members
=
[john, paul, ringo, george]
为什么不能这样做? 这是因为你不能只创建 Person和 Group之间的关联关系,你还要指定 Membership模型中所需要的所有信息;而简单的add、create 和赋值语句是做不到这一点的。所以它们不能在使用中介模型的多对多关系中使用。此时,唯一的办法就是创建中介模型的实例。
remove()方法被禁用也是出于同样的原因。但是clear() 方法却是可用的。它可以清空某个实例所有的多对多关系:
>>>
# Beatles have broken up
>>> beatles.members.clear()
>>>
# Note that this deletes the intermediate model instances
>>> Membership.objects.
all
()
[]
blogsystem项目中:我们也是通过创建实例来进行创建关系的代码如下:
def addArticle(request): if request.method=="POST": article_form = ArticleForm(request.POST) if article_form.is_valid(): title=article_form.cleaned_data.get("title") content=article_form.cleaned_data.get("content") personal_cate=request.POST.get("personal_cate") personal_tag=request.POST.getlist("personal_tag") article_obj=models.Article.objects.create(title=title,desc=content[0:30],create_time=datetime.datetime.now(),user=request.user,category_id=personal_cate) models.ArticleDetail.objects.create(content=content,article=article_obj) if personal_tag: # [2,3] for i in personal_tag: models.Article2Tag.objects.create(article_id=article_obj.nid,tag_id=i)#在第三张表中自动添加关系 else: pass return HttpResponse("添加成功") article_form=ArticleForm() cate_list=models.Category.objects.filter(blog__user=request.user) tag_list=models.Tag.objects.filter(blog__user=request.user) return render(request,"addArticle.html",{"article_form":article_form,"cate_list":cate_list,"tag_list":tag_list})
惰性查询
查询集 是惰性执行的 —— 创建查询集不会带来任何数据库的访问。你可以将过滤器保持一整天,直到查询集 需要求值时,Django 才会真正运行这个查询。
ret=models.Article.objects.all()这个就不会进入数据库查询print(ret) 就会对数据库进行查询一般来说,只有在“请求”查询集 的结果时才会到数据库中去获取它们。当你确实需要结果时,查询集 通过访问数据库来求值。 缓存机制:
每个查询集都包含一个缓存来最小化对数据库的访问。理解它是如何工作的将让你编写最高效的代码。
在一个新创建的查询集中,缓存为空。首次对查询集进行求值 —— 同时发生数据库查询 ——Django 将保存查询的结果到查询集的缓存中并返回明确请求的结果(例如,如果正在迭代查询集,则返回下一个结果)。接下来对该查询集 的求值将重用缓存的结果。
请牢记这个缓存行为,因为对查询集使用不当的话,它会坑你的。例如,下面的语句创建两个查询集,对它们求值,然后扔掉它们:
print([a.title
for
a
in
models.Article.objects.
all
()])
print([a.create_time
for
a
in
models.Article.objects.
all
()])
这意味着相同的数据库查询将执行两次,显然倍增了你的数据库负载。同时,还有可能两个结果列表并不包含相同的数据库记录,因为在两次请求期间有可能有Article被添加进来或删除掉。为了避免这个问题,只需保存查询集并重新使用它:
queryResult=models.Article.objects.
all
()
print([a.title
for
a
in
queryResult])
print([a.create_time
for
a
in
queryResult])
什么时候不会保存缓存?
查询集不会永远缓存它们的结果。当只对查询集的部分进行求值时会检查缓存, 如果这个部分不在缓存中,那么接下来查询返回的记录都将不会被缓存。所以,这意味着使用切片或索引来限制查询集将不会填充缓存。
例如,重复获取查询集对象中一个特定的索引将每次都查询数据库:
>>> queryset
=
Entry.objects.
all
()
>>>
print
queryset[
5
]
# Queries the database
>>>
print
queryset[
5
]
# Queries the database again
然而,如果已经对全部查询集求值过,则将检查缓存:
>>> queryset
=
Entry.objects.
all
()
>>> [entry
for
entry
in
queryset]
# Queries the database
>>>
print
queryset[
5
]
# Uses cache
>>>
print
queryset[
5
]
# Uses cache
下面是一些其它例子,它们会使得全部的查询集被求值并填充到缓存中:
>>> [entry
for
entry
in
queryset]
>>>
bool
(queryset)
>>> entry
in
queryset
>>>
list
(queryset)
注:简单地打印查询集不会填充缓存。
queryResult
=
models.Article.objects.
all
()
print
(queryResult)
# hits database
print
(queryResult)
# hits database
exists 和iterator
exists:
简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些 数据!为了避免这个,可以用exists()方法来检查是否有数据:
if queryResult.exists(): #SELECT (1) AS "a" FROM "blog_article" LIMIT 1; args=() print("exists...")
iterator:
当queryset非常巨大时,cache会成为问题。
处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法 来获取数据,处理完数据就将其丢弃。
objs = Book.objects.all().iterator() # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 for obj in objs: print(obj.title) #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了 for obj in objs: print(obj.title)当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用#iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询
总结:
queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
查询优化
对于一对一字段(OneToOneField)和外键字段(ForeignKey),可以使用select_related 来对QuerySet进行优化。
select_related 返回一个QuerySet,当执行它的查询时它沿着外键关系查询关联的对象的数据。它会生成一个复杂的查询并引起性能的损耗,但是在以后使用外键关系时将不需要数据库查询。
简单说,在对QuerySet使用select_related()函数后,Django会获取相应外键对应的对象,从而在之后需要的时候不必再查询数据库了。
obj_list=models.Article.objects.select_related("user").select_related("category").all()print(obj_list)for i in obj_list: print(i.category)for i in obj_list: print(i.title)
cbv视图:
通过类来对应url 通过类里面的方法来对应
views代码:
from django.views import View class Login(View): def get(self,request): return render(request,"login_cbv.html") def post(self,request): return HttpResponse("post.........") def delete(self): pass