2013-11-29 15 views
5

Mam do czynienia z bardzo dziwnym problemem w jednym z moich projektów django. W moim projekcie mam niestandardową klasę pola, która obsługuje klucze obce, jeden do jednego i wiele 2 wielu pól modelu. Klasa to coś takiego jak poniżej.Django Niestandardowe atrybuty pól generujące zapytania do bazy danych

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 

Ostatnio gdy grałem z django-toolbar, dowiedziałem się jedną ze stron, które ma formę z wyżej wymienionych dziedzin była śmiesznie ponownie i ponownie podejmowania wielu zapytań do tych samych obiektów.

enter image description here

podczas debugowania, dowiedziałem się metoda prepare_value był nazywany ponownie. Po kilku debugowaniu zdałem sobie sprawę, że sprawcą był szablon. Mam ogólny szablon, który używam do formy, to wygląda mniej więcej tak:

{% 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 %} 

W powyższym kodzie, każda instrukcja if zwraca klasę dziedzinie, która wywołuje metodę prepare_value które następnie sprawia kwerend bazodanowych. Każdy z wymienionych poniżej tworzy zapytanie do bazy danych, jestem całkowicie zagubiony, dlaczego tak się dzieje i nie mam pojęcia o żadnych rozwiązaniach. Każda pomoc, sugestie będą naprawdę mile widziane. Dzięki.

  • field.is_hidden
  • field.field.required
  • field.label
  • field.label_tag
  • pole
  • field.field.widget.attrs.help_text
  • pola. błędy

Ponadto, dlaczego t jego dzieje się tylko z moją niestandardową klasą pola, innymi polami (FK, O2O, M2M) w aplikacji i administratorem aplikacji, po prostu wykonaj jedno zapytanie, nawet jeśli używasz podobnego szablonu.

+0

Jaką wersję django używasz? – aquavitae

+0

Wersja Django - (1, 5, 5, "końcowa", 0) – Amyth

Odpowiedz

5

Problem dotyczy metody prepare_value(), która wykonuje jawne zapytania. .get() nie jest buforowany i zawsze trafia do bazy danych podczas iterowania w zestawie zapytań .filter(). Może to powodować wiele zapytań.

Nie jest to widoczne w polach domyślnych, ponieważ nie zawierają żadnych zapytań w prepare_value().

Aby rozwiązać ten problem, można spróbować zapisać w pamięci podręcznej numery value i result. Jeśli value nie ulegnie zmianie, zwróć wynik w pamięci podręcznej. Coś takiego:

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 

Nie jestem pewien, jak dobrze/źle to działa!

+0

Dzięki, wydaje się, że naprawiliśmy problem. Mam zamiar przetestować to przez chwilę i jeśli wszystko wydaje się w porządku, nagrodzi cię nagrodą – elssar

Powiązane problemy