2009-11-13 13 views
9

Uno dei miei modelli ha attributi che non sono memorizzati nel database. Tutto va bene alla vista e al livello del modello, ma non riesco a visualizzare questi attributi 'non-database' nel mio modello.Creazione di attributi transitori (non di database) nel modello Django disponibile per il modello

Ecco un esempio di codice, un esempio artificiale che rispecchia l'effettivo dominio del problema, per dimostrare il comportamento indesiderato.

La vista:

def odometer(request): 
    cars = Car.objects.all() 
    for car in cars: 
     car.read_meters() 
    context = {'cars': cars} 
    return render_to_response('odometer.html', context) 

I modelli:

class Car(models.Model): 
    name = models.CharField(_('name'), max_length=100, unique=True) 

    def read_meters(self): 
     for meter in self.meter_set.all(): 
      meter.read() 

    def __unicode__(self): 
     return '%s' % self.name 

class Meter(models.Model): 
    name = models.CharField(_('name'), max_length=100) 
    car = models.ForeignKey(Car) 

    difference = 0 
    previous = 0 
    changed = False 

    def read(self): 
     # this is completely artificial. in the real application we would interface with the hardware 
     # meter to get data 
     try: 
      previous_count = MeterReading.objects.filter(meter__id=self.id).order_by('-stamp')[0].count 
     except: 
      previous_count = 0 
     self.previous = previous_count 
     current_count = previous_count 

     if (random.randrange(0, 2) == 0): 
      self.difference = int(random.random() * 100) 
      if self.name == 'Odometer' or (random.randrange(0, 2) == 0): 
       current_count += self.difference 
      else: 
       current_count -= self.difference 
       if current_count < 0: 
        current_count = 0 
     if current_count > previous_count: 
      self.changed = True 
     new_reading = MeterReading() 
     new_reading.count = current_count 
     new_reading.meter = self 
     new_reading.save() 

    def __unicode__(self): 
     return '%s' % self.name 

class MeterReading(models.Model): 
    count = models.IntegerField(_('count')) 
    stamp = models.DateTimeField(editable=False, auto_now_add=True) 
    meter = models.ForeignKey(Meter) 
    def __unicode__(self): 
     return '%s' % self.count 

E il modello:

{% for car in cars %} 
    <h2>{{ car }}</h2> 
    {% for meter in car.meter_set.all %} 
    <h3>{{ meter }}</h3> 
    <p>Difference: {{ meter.difference }}</p> 
    <p>Changed: {{ meter.changed }}</p> 
    <ul> 
     {% for reading in meter.meterreading_set.all %} 
     <li>{{ reading }}</li> 
     {% endfor %} 
    </ul> 
    {% endfor %} 
{% endfor %} 

Il problema è 'meter.difference' e 'meter.changed' don 't produrre i valori aggiornati corretti. Che cosa sto facendo di sbagliato? Qualsiasi consiglio è apprezzato.

Grazie.

UPDATE: Aggiornato il codice in base alla risposta di Daniel:

il modello di auto:

class Car(models.Model): 
    name = models.CharField(_('name'), max_length=100, unique=True) 

    def read_meters(self): 
     for meter in self.meters: 
      meter.read() 

    def __unicode__(self): 
     return '%s' % self.name 

    @property 
    def meters(self): 
     if not hasattr(self, '_meters'): 
      self._meters = self.meter_set.all() 
     return self._meters 

E il modello:

{% for car in cars %} 
    <h2>{{ car }}</h2> 
    {% for meter in car.meters %} 
    <h3>{{ meter }}</h3> 
    <p>{{ meter.name }} difference: {{ meter.difference }}</p> 
    <p>Changed: {{ meter.changed }}</p> 
    <ul> 
     {% for reading in meter.meterreading_set.all %} 
     <li>{{ reading }}</li> 
     {% endfor %} 
    </ul> 
    {% endfor %} 
{% endfor %} 

risposta

11

Il motivo per cui non si visualizzano questi valori nel modello è che ogni volta che si chiama car.meter_set.all() si ottiene un queryset completamente nuovo direttamente dal database.

Le istanze di modello di Django non hanno identità, quindi anche se gli oggetti Meter in un set di query hanno gli stessi valori di database di quelli in un altro, non condividono alcun attributo dinamico.

Un modo per farlo sarebbe quello di memorizzare nella cache gli oggetti del misuratore all'interno di ogni automobile, poiché mostro here in una domanda recente. Quindi, invece di fare riferimento a car.meter_set.all() nella vista, modello e modello, si farebbe car.get_meters() o qualsiasi altra cosa, e si otterrebbe lo stesso insieme di oggetti ogni volta, insieme agli attributi dinamici.

+0

Fantastico grazie. –

1

ho provato qualcosa di simile nel mio codice e sembra come il problema che stai incontrando sta accadendo nel metodo read_meter() e nella vista odomoter().

Gli oggetti meter e car che si sta utilizzando per eseguire un'iterazione tramite i segmenti di query non rientrano nell'ambito e le modifiche apportate ai relativi attributi vengono seguite.

Quando si visualizza meter.difference e meter.changed nel modello, Django ricrea gli oggetti dal DB (e senza i valori degli attributi non salvati).

Spero che la spiegazione sia chiara. Qualche ragione per non salvare i valori nel DB?

+0

Il motivo per cui non ho salvato i dati è semplicemente che è transitorio. Mi sembra semplicemente sbagliato memorizzare dati che dovranno essere calcolati al volo e non verranno mai più referenziati in un campo di database.Si prega di notare anche che l'esempio sopra è artificiale e nella reale applicazione alcuni dei dati transitori sono centinaia di MB di dimensioni. Grazie per l'aiuto. –

Problemi correlati