2013-11-29 56 views
5

我在我的一个Django项目中遇到了一个非常奇怪的问题。在我的项目中,我有一个处理外键的自定义字段类,一对一和许多2个模型字段。这个班有一些像下面这样的东西。Django自定义字段的属性使数据库查询

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玩,我发现有上述领域一个形式可笑连连庄家对同一对象的多个查询的页面之一。

enter image description here

调试时,我发现了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
  • field.label_tag
  • field.field.widget.attrs.help_text
  • 场。错误

此外,为什么t他仅在我的自定义字段类,应用程序和应用程序管理员中的其他字段(FK,O2O,M2M)发生时,只进行一次查询,即使他们正在使用类似的模板。

+0

你用的是什么版本的django? – aquavitae

+0

Django版本 - (1,5,5,'final',0) – Amyth

回答

5

问题出在您的prepare_value()方法中,它会显式查询。 .get()没有得到缓存,并且总是在db上迭代,queryset将对此进行评估。 这可能会导致您多次查询。

这在默认字段中没有看到,因为它们在prepare_value()中没有执行任何查询。

要解决此问题,您可以尝试缓存valueresult。如果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 

不知道这个解决方案有多好/不好!

+0

谢谢,这似乎解决了这个问题。我会测试一段时间,如果一切似乎没问题,都会奖赏你的赏金 – elssar

相关问题