我在我的一个 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
最近我在玩 django-toolbar ,我发现其中一个包含上述字段的表单的页面荒谬地一次又一次地对相同的对象进行多次查询。
在调试时,我发现 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 %}
在上面的代码中,每个if
语句调用字段类,该字段类调用prepare_value
方法,然后进行数据库查询。下面列出的每一个都在进行数据库查询,我完全不知道为什么会这样,也不知道任何解决方案。任何帮助,建议将不胜感激。谢谢。
- field.is_hidden
- field.field.required
- 字段标签
- field.label_tag
- 领域
- field.field.widget.attrs.help_text
- 字段错误
另外,为什么只有我的自定义字段类会发生这种情况,应用程序中的其他字段(FKs、O2Os、M2M's)和应用程序管理员只需进行一次查询,即使它们使用的是类似的模板。
最佳答案
问题出在执行显式查询的 prepare_value()
方法上。 .get()
不会被缓存,并且在 .filter()
查询集上迭代时总是命中数据库将对其进行评估。
这可能会导致您多次查询。
这在默认字段中是看不到的,因为它们不在 prepare_value()
中进行任何查询。
要解决这个问题,您可以尝试缓存value
和result
。如果 value
没有改变,返回缓存的结果。像这样的东西:
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
虽然不确定这个解决方案有多好/多坏!
关于database - Django 自定义字段的属性进行数据库查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20279674/