I am facing a very weird problem in one of my django projects. In my project I have a custom field class that handles foreign keys, one to one and many 2 many model fields. The class is some thing like the following.
我在我的一个django项目中遇到了一个非常奇怪的问题。在我的项目中,我有一个自定义字段类,处理外键,一对一和许多2个模型字段。该课程如下所示。
from django import forms
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.model = model
self.limit = limit
self.multiple = multiple
self.create_objects = create_objects
super(CustomRelatedField, self).__init__(*args, **kwargs)
def clean(self, value):
""" Calls self.get_objects to get the actual model object instance(s)
from the given unicode value.
"""
# Do some value processing here
return self.get_objects(value)
def get_objects(self, values):
""" Returns the model object instances for the given unicode values.
"""
results = []
for value in values:
try:
obj = self.model.object.get_or_create(name=value)[0]
results.append(obj)
except Exception, err:
# Log the error here.
return results
def prepare_value(self, value):
""" Returns the value to be sent to the UI. The value
passed to this method is generally the object id or
a list of object id's (in case it is a many to many object).
So we need to make a database query to get the object and
then return the name attribute.
"""
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
return result
Recently while I was playing with the django-toolbar, I found out one of the pages that has a form with the above mentioned fields was ridiculously making multiple queries for the same objects again and again.
最近,当我在使用django工具栏时,我发现其中一个页面上面有一个带有上述字段的表单,一次又一次地对同一个对象进行多次查询。
While debugging, I found out the prepare_value
method was being called again and again. After some more debugging, I realized the culprit was the template. I have a generic template that I use for forms, It looks something like the following:
在调试时,我发现一次又一次地调用prepare_value方法。经过一些调试后,我意识到罪魁祸首是模板。我有一个用于表单的通用模板,它看起来像下面这样:
{% for field in form %}
{% if field.is_hidden %}
<!-- Do something here -->
{% endif %}
{% if field.field.required %}
<!-- Do something here -->
{% endif %}
<label>{{ field.label }}</label>
<div class="form-field">{{ field }}</div>
{% if field.field.widget.attrs.help_text %}
<!-- Do something here -->
{% elif field.errors %}
<!-- Do something here -->
{% endif %}
{% endfor %}
In the above code, each if
statement calls the field class which calls the prepare_value
method which then makes the database queries. Each of the following listed is making a database query, I am totally lost to why this is happening and have no clue about any solutions. Any help, suggestions would be really appreciated. Thanks.
在上面的代码中,每个if语句都调用调用prepare_value方法的字段类,然后该方法进行数据库查询。下面列出的每一个都是进行数据库查询,我完全不知道为什么会发生这种情况并且对任何解决方案都没有任何线索。任何帮助,建议将非常感激。谢谢。
- field.is_hidden
- field.is_hidden
- field.field.required
- field.field.required
- field.label
- field.label
- field.label_tag
- field.label_tag
- field
- 领域
- field.field.widget.attrs.help_text
- field.field.widget.attrs.help_text
- field.errors
- field.errors
Also, why does this happen with my custom field class only, other fields (FKs, O2Os, M2M's) in the application and the application admin, just make one query, even though they are using a similar template.
此外,为什么这只发生在我的自定义字段类,应用程序中的其他字段(FK,O2O,M2M)和应用程序管理员,只需进行一次查询,即使他们使用的是类似的模板。
1 个解决方案
#1
5
Problem is with your prepare_value()
method which does explicit queries. .get()
does not get cached and always hits the db while iterating on .filter()
queryset will evaluate that. This might be causing you multiple queries.
问题在于您的prepare_value()方法执行显式查询。 .get()不会被缓存并且总是在迭代时命中数据库.filter()查询集将对此进行评估。这可能会导致您进行多次查询。
This is not seen in default fields because they do not do any queries in prepare_value()
.
这在默认字段中看不到,因为它们不在prepare_value()中执行任何查询。
To resolve this, you can try to cache the value
and result
. If value
hasn't changed, return cached result. Something like:
要解决此问题,您可以尝试缓存值和结果。如果值未更改,则返回缓存结果。就像是:
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.cached_result = None
self.cached_value = None
...
def prepare_value(self, value):
#check we have cached result
if value == self.cached_value:
return self.cached_result
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
#cache the result and value
self.cached_result = result
self.cached_value = value
return result
Not sure how good/bad this work around though!
不知道这个工作有多好/坏!
#1
5
Problem is with your prepare_value()
method which does explicit queries. .get()
does not get cached and always hits the db while iterating on .filter()
queryset will evaluate that. This might be causing you multiple queries.
问题在于您的prepare_value()方法执行显式查询。 .get()不会被缓存并且总是在迭代时命中数据库.filter()查询集将对此进行评估。这可能会导致您进行多次查询。
This is not seen in default fields because they do not do any queries in prepare_value()
.
这在默认字段中看不到,因为它们不在prepare_value()中执行任何查询。
To resolve this, you can try to cache the value
and result
. If value
hasn't changed, return cached result. Something like:
要解决此问题,您可以尝试缓存值和结果。如果值未更改,则返回缓存结果。就像是:
class CustomRelatedField(forms.Field):
def __init__(self, model, limit=None, multiple=False, create_objects=True, *args, *kwargs):
self.cached_result = None
self.cached_value = None
...
def prepare_value(self, value):
#check we have cached result
if value == self.cached_value:
return self.cached_result
if self.multiple:
result = [obj.name for obj in self.model.filter(pk__in=value)]
else:
result = self.model.object.get(pk=value)
#cache the result and value
self.cached_result = result
self.cached_value = value
return result
Not sure how good/bad this work around though!
不知道这个工作有多好/坏!