Phi*_*ord 5 python django many-to-many
我正在使用带有'through'类的ManyToManyField,这在获取事物列表时会导致大量查询.我想知道是否有更有效的方式.
例如,这里有一些描述Books及其几个作者的简化类,它们通过Role类(定义"Editor","Illustrator"等角色):
class Person(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
@property
def full_name(self):
return ' '.join([self.first_name, self.last_name,])
class Role(models.Model):
name = models.CharField(max_length=50)
person = models.ForeignKey(Person)
book = models.ForeignKey(Book)
class Book(models.Model):
title = models.CharField(max_length=255)
authors = models.ManyToManyField(Person, through='Role')
@property
def authors_names(self):
names = []
for role in self.role_set.all():
person_name = role.person.full_name
if role.name:
person_name += ' (%s)' % (role.name,)
names.append(person_name)
return ', '.join(names)
Run Code Online (Sandbox Code Playgroud)
如果我调用Book.authors_names(),那么我可以得到这样的字符串:
John Doe(编辑),Fred Bloggs,Billy Bob(插画家)
它工作正常,但它执行一个查询以获取该书的角色,然后为每个人执行另一个查询.如果我正在显示一个书籍列表,这会增加很多查询.
有没有办法在每本书的单个查询中使用连接更有效地执行此操作?或者是使用批量选择之类的东西的唯一方法?
(对于奖励积分......我对authors_names()的编码看起来有点笨重 - 有没有办法让它更优雅地使用Python-esque?)
这是我经常在Django遇到的模式.创建像你这样的属性非常容易,author_name当你显示一本书时它们很有用,但是当你想在页面上使用该属性来处理很多书时,查询的数量就会爆炸.
首先,您可以使用select_related以防止查找每个人
for role in self.role_set.all().select_related(depth=1):
person_name = role.person.full_name
if role.name:
person_name += ' (%s)' % (role.name,)
names.append(person_name)
return ', '.join(names)
Run Code Online (Sandbox Code Playgroud)
但是,这并没有解决查找每本书角色的问题.
如果要显示书籍列表,则可以在一个查询中查找书籍的所有角色,然后缓存它们.
>>> books = Book.objects.filter(**your_kwargs)
>>> roles = Role.objects.filter(book_in=books).select_related(depth=1)
>>> roles_by_book = defaultdict(list)
>>> for role in roles:
... roles_by_book[role.book].append(books)
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过roles_by_dict字典访问图书的角色.
>>> for book in books:
... book_roles = roles_by_book[book]
Run Code Online (Sandbox Code Playgroud)
您将不得不重新考虑您的author_name属性以使用这样的缓存.
我也会拍摄奖励积分.
向角色添加方法以呈现全名和角色名称.
class Role(models.Model):
...
@property
def name_and_role(self):
out = self.person.full_name
if self.name:
out += ' (%s)' % role.name
return out
Run Code Online (Sandbox Code Playgroud)
该author_names崩溃的一个衬垫类似保罗的建议
@property
def authors_names(self):
return ', '.join([role.name_and_role for role in self.role_set.all() ])
Run Code Online (Sandbox Code Playgroud)