Обеспечение доступа к шаблону временных (не связанных с базой данных) атрибутов в модели Django - PullRequest
9 голосов
/ 13 ноября 2009

Одна из моих моделей имеет атрибуты, которые не хранятся в базе данных. Все хорошо на уровне представления и модели, но я не могу отобразить эти атрибуты «не в базе данных» в моем шаблоне.

Вот пример кода, искусственного примера, который отражает реальную проблемную область, чтобы продемонстрировать нежелательное поведение.

Вид:

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

Модели:

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

И шаблон:

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

Проблема в том, что «meter.difference» и «meter.changed» не выводят правильные обновленные значения. Что я делаю неправильно? Любой совет приветствуется.

Спасибо.

ОБНОВЛЕНИЕ: обновлен код, основанный на ответе Даниила:

Модель автомобиля:

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

И шаблон:

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

Ответы [ 2 ]

11 голосов
/ 13 ноября 2009

Причина, по которой вы не видите эти значения в своем шаблоне, заключается в том, что каждый раз, когда вы вызываете car.meter_set.all(), вы получаете совершенно новый набор запросов прямо из базы данных.

Экземпляры модели Django не имеют идентичности, поэтому, хотя объекты Meter в одном наборе запросов имеют те же значения базы данных, что и в другом, они не имеют общих динамических атрибутов.

Один из способов сделать это - кэшировать объекты Метра в каждой машине, как я показываю здесь в недавнем вопросе. Тогда вместо ссылки на car.meter_set.all() в представлении, модели и шаблоне вы должны сделать car.get_meters() или что-то еще, и вы будете каждый раз получать один и тот же набор объектов вместе с вашими динамическими атрибутами.

1 голос
/ 13 ноября 2009

Я попробовал нечто подобное в своем собственном коде, и похоже, что проблема, с которой вы сталкиваетесь, происходит в методе read_meter() и представлении odomoter().

Объекты meter и car, которые вы используете для итерации по QuerySets, выпадают из области видимости, и изменения, которые вы вносите в их атрибуты, идут вместе с ними.

Когда вы отображаете meter.difference и meter.changed в шаблоне, Django воссоздает эти объекты из БД (и без несохраненных значений атрибутов).

Надеюсь, это объяснение понятно. Любая причина не сохранять значения в БД?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...