如何优化 django 中自引用模型中相关记录的检索?
我正在从模型创建表单。该表单包含一个类别。类别模型是一个自引用模型。我想在表单的下拉列表中显示类别,以便它们也显示其父类别。即,
parentcat3 >父猫2>父猫1> leafcategory
最后一个类别由 leafCategory=True
表示。
我已经重写了我的 ModelForm 以仅选择叶(结束)类别的记录,使用;
self.fields['primaryCategory'].queryset = Category.objects.exclude(leafCategory=False)
然后,ModelForm 下拉列表使用模型中 __unicode__
的返回值:
getFullPathString()
这会沿着“树”向上查找父类别。这是慢的部分(有10,000个类别)
如何优化父记录的检索?
这是我正在使用的代码:
模型
class Category(models.Model):
name = models.CharField(max_length=200)
parent = models.ForeignKey("self",related_name='related_category',null=True)
leafCategory=models.BooleanField(default=False)
def __unicode__(self):
return u"%s" % self.getFullPathString()
def getParentArray(self):
"""Create an array of parent objects from the current category """
itemArray=[]
itemArray.insert(0,self)
parentCat=self.parent
while parentCat:
itemArray.insert(0,parentCat)
parentCat=parentCat.parent
return itemArray
def getFullPathString(self):
"""Get the parent category names, cat 1 > cat 2 etc"""
returnText=""
for item in self.getParentArray():
returnText += item.name
if not item.isLeaf():
returnText += " > "
return returnText
表格
class InventoryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(InventoryForm, self).__init__(*args, **kwargs)
self.fields['primaryCategory'].queryset = Category.objects.exclude(leafCategory=False)
I am creating a form from a model. This form contains a category. The category model is a self referencing model. I want to display the categories in a dropdown on the form so they show their parent categories too. ie,
parentcat3 > parentcat2 > parentcat1 > leafcategory
the last category is denoted by leafCategory=True
.
I have overridden my ModelForm to select records only that are leaf (end) categories using;
self.fields['primaryCategory'].queryset = Category.objects.exclude(leafCategory=False)
The ModelForm dropdown then uses the return value from __unicode__
in the model which is;
getFullPathString()
This walks back up the 'tree' to find the parent categories. This is the part that is slow (there are 10,000 categories)
How can I optimize the retrieval of the parent records?
Here is the code I am using:
Model
class Category(models.Model):
name = models.CharField(max_length=200)
parent = models.ForeignKey("self",related_name='related_category',null=True)
leafCategory=models.BooleanField(default=False)
def __unicode__(self):
return u"%s" % self.getFullPathString()
def getParentArray(self):
"""Create an array of parent objects from the current category """
itemArray=[]
itemArray.insert(0,self)
parentCat=self.parent
while parentCat:
itemArray.insert(0,parentCat)
parentCat=parentCat.parent
return itemArray
def getFullPathString(self):
"""Get the parent category names, cat 1 > cat 2 etc"""
returnText=""
for item in self.getParentArray():
returnText += item.name
if not item.isLeaf():
returnText += " > "
return returnText
Form
class InventoryForm(ModelForm):
def __init__(self, *args, **kwargs):
super(InventoryForm, self).__init__(*args, **kwargs)
self.fields['primaryCategory'].queryset = Category.objects.exclude(leafCategory=False)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这段代码效率极其低下。它将针对每个类别以及每个类别的祖先执行一系列新的数据库查询。
您需要研究针对在数据库中存储和检索此类分层数据进行优化的算法。 django-mptt 是我最喜欢的。
顺便说一句,在列表的开头重复插入元素也是低效的。列表针对附加进行了优化,但并未针对插入进行优化。为在两端添加而优化的数据结构是 Python 的
collections
模块中的deque
- 但更好的解决方案可能是简单地附加元素,然后调用reverse ()
在返回列表之前。This code is incredibly inefficient. It will do a new series of database queries for every single category, and for each category's ancestor.
You need to look into the algorithms that are optimised for storing and retrieving this sort of hierarchical data in a database. django-mptt is my favourite of these.
As an aside, it is also inefficient to repeatedly insert elements at the start of a list. Lists are optimised for appending, they are not optimised for inserting. A data structure optimised for adding at both ends is the
deque
in Python'scollections
module - but a better solution would probably be to simply append the elements, then callreverse()
on the list before returning it.