2009-11-13 2 views
9

Eines meiner Modelle hat Attribute, die nicht in der Datenbank gespeichert sind. Auf der Ansichts- und Modellebene ist alles in Ordnung, aber ich kann diese "Nicht-Datenbank" -Attribute nicht in meiner Vorlage anzeigen.Transiente (nicht Datenbank-) Attribute im Django-Modell für die Vorlage verfügbar machen

Hier ist ein Beispielcode, ein künstliches Beispiel, das die eigentliche Problemdomäne widerspiegelt, um das unerwünschte Verhalten zu demonstrieren.

Aussicht:

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

Die Modelle:

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 

Und die Vorlage:

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

Das Problem ist 'meter.difference' und 'meter.changed' don t die korrekten aktualisierten Werte ausgeben. Was mache ich falsch? Jeder Rat wird geschätzt.

Danke.

UPDATE: aktualisierte Code basiert auf Daniels Antwort:

Das Fahrzeugmodell:

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 

Und die Vorlage:

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

Antwort

11

Der Grund, warum Sie diese Werte in Ihrer Vorlage nicht sehen, besteht darin, dass Sie jedes Mal, wenn Sie car.meter_set.all() aufrufen, ein komplett neues Queryset direkt aus der Datenbank erhalten.

Django-Modellinstanzen haben keine Identität. Daher haben die Meter-Objekte in einem Abfrage-Set dieselben Datenbankwerte wie die in einem anderen, sie teilen keine dynamischen Attribute.

Eine Möglichkeit, dies zu tun wäre, die Meter-Objekte in jedem Auto zu cachen, wie ich here auf einer aktuellen Frage zeigen. Dann statt car.meter_set.all() in der Ansicht, Modell und Vorlage, würden Sie car.get_meters() oder was auch immer tun, und Sie würden die gleiche Menge von Objekten jedes Mal, zusammen mit Ihren dynamischen Attributen.

+0

Super danke. –

1

ich etwas ähnliches in meinem eigenen Code versucht, und es sieht aus Wie das Problem, in das Sie geraten, passiert in der read_meter() Methode und odomoter() Ansicht.

Die meter und car Objekte, die Sie verwenden, durch die QuerySets iterieren fallen aus der Umfang und die Änderungen ihrer Eigenschaften machen mit ihnen gehen.

Wenn Sie meter.difference und meter.changed in der Vorlage anzeigen, erstellt Django diese Objekte aus der DB (und ohne die nicht gespeicherten Attributwerte).

Hoffe, dass die Erklärung klar ist. Gibt es einen Grund, die Werte nicht in der Datenbank zu speichern?

+0

Mein Grund für das Nichtspeichern der Daten ist einfach, dass es vorübergehend ist. Es scheint mir einfach falsch zu sein, Daten zu speichern, die im laufenden Betrieb berechnet werden müssen und nie wieder in einem Datenbankfeld referenziert werden.Bitte beachten Sie auch, dass das obige Beispiel künstlich ist und in der realen Anwendung einige der transienten Daten Hunderte von MBs groß sind. Danke für Ihre Hilfe. –

Verwandte Themen