2013-11-29 10 views
5

Sto affrontando un problema molto strano in uno dei miei progetti di Django. Nel mio progetto ho una classe di campo personalizzata che gestisce le chiavi esterne, uno a uno e molti altri due campi del modello. La classe è qualcosa come la seguente.Django Attributi del campo personalizzato che effettuano query di database

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 

Recentemente, mentre stavo giocando con la django-toolbar, ho scoperto che una delle pagine che ha un modulo con i campi di cui sopra è stato ridicolmente rendendo più query per gli stessi oggetti più e più volte.

enter image description here

Durante il debug, ho scoperto il metodo prepare_value era stato chiamato ancora e ancora. Dopo un po 'di debug, ho capito che il colpevole era il modello. Ho un modello generico che uso per forme, Sembra qualcosa di simile al seguente:

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

Nel codice sopra, ogni if dichiarazione chiama la classe campo che chiama il metodo prepare_value che poi rende le query di database. Ognuno dei seguenti elencati sta facendo una query sul database, sono totalmente perso dal motivo per cui questo sta accadendo e non ho idea di nessuna soluzione. Qualsiasi aiuto, suggerimenti sarebbe molto apprezzato. Grazie.

  • field.is_hidden
  • field.field.required
  • field.label
  • field.label_tag
  • campo
  • field.field.widget.attrs.help_text
  • campo. errori

Inoltre, perché non t il suo accadere solo con la mia classe campo personalizzata, altri campi (FK, O2O, M2M) nell'applicazione e l'amministratore dell'applicazione, basta fare una query, anche se stanno usando un modello simile.

+0

Quale versione di django stai usando? – aquavitae

+0

Versione Django - (1, 5, 5, 'finale', 0) – Amyth

risposta

5

Il problema è relativo al metodo prepare_value() che esegue query esplicite. .get() non viene memorizzato nella cache e colpisce sempre il db mentre iterating su .filter() queryset lo valuterà. Questo potrebbe causare più query.

Questo non è visualizzato nei campi predefiniti perché non eseguono alcuna query in prepare_value().

Per risolvere questo problema, è possibile provare a memorizzare nella cache il value e result. Se value non è stato modificato, restituire il risultato memorizzato nella cache. Qualcosa di simile:

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 

Non sono sicuro di quanto bene/male questo funziona comunque!

+0

Grazie, sembra aver risolto il problema. Lo metterò alla prova per un po 'e se tutto sembra ok, ti ​​assegnerò la taglia – elssar

Problemi correlati