使Django模型中的瞬态(非数据库)属性可用于模板

时间:2022-03-17 20:21:15

One of my models has attributes that are not stored in the database. Everything is fine at the view and model level but I cannot seem to display these 'non-database' attributes in my template.

我的一个模型具有未存储在数据库中的属性。在视图和模型级别一切都很好,但我似乎无法在我的模板中显示这些“非数据库”属性。

Here's some example code, an artificial example that mirrors the actual problem domain, to demonstrate the undesired behavior.

下面是一些示例代码,一个反映实际问题域的人工示例,用于演示不需要的行为。

The view:

风景:

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

The models:

型号:

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

And the template:

和模板:

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

The problem is 'meter.difference' and 'meter.changed' don't output the correct updated values. What am I doing wrong? Any advice is appreciated.

问题是'meter.difference'和'meter.changed'没有输出正确的更新值。我究竟做错了什么?任何建议表示赞赏。

Thanks.

谢谢。

UPDATE: Updated code based on Daniel's answer:

更新:根据Daniel的回答更新了代码:

The Car model:

汽车型号:

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

And the template:

和模板:

{% 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 个解决方案

#1


11  

The reason why you aren't seeing these values in your template is that every time you call car.meter_set.all() you get a completely new queryset straight from the database.

您没有在模板中看到这些值的原因是每次调用car.meter_set.all()时,您都会直接从数据库中获得一个全新的查询集。

Django model instances don't have identity, so even though the Meter objects in one queryset have the same database values as the ones in another, they do not share any dynamic attributes.

Django模型实例没有标识,因此即使一个查询集中的Meter对象与另一个查询集中的Meter对象具有相同的数据库值,它们也不共享任何动态属性。

One way to do this would be to cache the Meter objects within each Car, as I show here on a recent question. Then instead of referring to car.meter_set.all() in the view, model and template, you would do car.get_meters() or whatever, and you would get the same set of objects each time, along with your dynamic attributes.

一种方法是在每个Car中缓存Meter对象,正如我在最近的一个问题上所示。然后,不是在视图,模型和模板中引用car.meter_set.all(),而是执行car.get_meters()或其他任何操作,并且每次都会获得相同的对象集以及动态属性。

#2


1  

I tried something similar in my own code and it looks like the problem you're running into is happening in the read_meter() method and odomoter() view.

我在自己的代码中尝试了类似的东西,看起来你遇到的问题发生在read_meter()方法和odomoter()视图中。

The meter and car objects that you're using to iterate through the QuerySets are falling out of scope, and the changes you make to their attributes is going with them.

您用于迭代QuerySets的仪表和汽车对象超出范围,您对其属性所做的更改将随之发生。

When you display meter.difference and meter.changed in the template, Django's recreating those objects from the DB (and without the unsaved attribute values).

当您在模板中显示meter.difference和meter.changed时,Django将从DB重新创建这些对象(并且没有未保存的属性值)。

Hope that explanation is clear. Any reason not to save the values to the DB?

希望解释清楚。有什么理由不将值保存到DB?

#1


11  

The reason why you aren't seeing these values in your template is that every time you call car.meter_set.all() you get a completely new queryset straight from the database.

您没有在模板中看到这些值的原因是每次调用car.meter_set.all()时,您都会直接从数据库中获得一个全新的查询集。

Django model instances don't have identity, so even though the Meter objects in one queryset have the same database values as the ones in another, they do not share any dynamic attributes.

Django模型实例没有标识,因此即使一个查询集中的Meter对象与另一个查询集中的Meter对象具有相同的数据库值,它们也不共享任何动态属性。

One way to do this would be to cache the Meter objects within each Car, as I show here on a recent question. Then instead of referring to car.meter_set.all() in the view, model and template, you would do car.get_meters() or whatever, and you would get the same set of objects each time, along with your dynamic attributes.

一种方法是在每个Car中缓存Meter对象,正如我在最近的一个问题上所示。然后,不是在视图,模型和模板中引用car.meter_set.all(),而是执行car.get_meters()或其他任何操作,并且每次都会获得相同的对象集以及动态属性。

#2


1  

I tried something similar in my own code and it looks like the problem you're running into is happening in the read_meter() method and odomoter() view.

我在自己的代码中尝试了类似的东西,看起来你遇到的问题发生在read_meter()方法和odomoter()视图中。

The meter and car objects that you're using to iterate through the QuerySets are falling out of scope, and the changes you make to their attributes is going with them.

您用于迭代QuerySets的仪表和汽车对象超出范围,您对其属性所做的更改将随之发生。

When you display meter.difference and meter.changed in the template, Django's recreating those objects from the DB (and without the unsaved attribute values).

当您在模板中显示meter.difference和meter.changed时,Django将从DB重新创建这些对象(并且没有未保存的属性值)。

Hope that explanation is clear. Any reason not to save the values to the DB?

希望解释清楚。有什么理由不将值保存到DB?