【问题标题】:Making transient (non-database) attributes in Django model available to template使 Django 模型中的瞬态(非数据库)属性可用于模板
【发布时间】:2009-11-13 04:32:58
【问题描述】:

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

这里有一些示例代码,一个反映实际问题域的人工示例,用于演示不良行为。

观点:

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

【问题讨论】:

  • 我会避免添加动态属性。我宁愿尝试制作differencechanged 方法。

标签: django oop django-models


【解决方案1】:

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

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

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

【讨论】:

    【解决方案2】:

    我在自己的代码中尝试了类似的方法,您遇到的问题似乎发生在 read_meter() 方法和 odomoter() 视图中。

    您用于遍历 QuerySet 的 metercar 对象已超出范围,您对其属性所做的更改也会随之发生。

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

    希望解释清楚。有什么理由不将值保存到数据库中?

    【讨论】:

    • 我不保存数据的原因仅仅是它是暂时的。存储必须即时计算并且永远不会在数据库字段中再次引用的数据对我来说似乎是错误的。另请注意,上面的示例是人为的,在实际应用中,一些瞬态数据的大小为数百 MB。感谢您的帮助。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-08-10
    • 1970-01-01
    • 2016-03-17
    • 2015-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多