2009-11-13 37 views
9

我的模型之一具有未存储在数据库中的属性。视图和模型级别的一切都很好,但我似乎无法在模板中显示这些“非数据库”属性。使Django模型中的瞬态(非数据库)属性可用于模板

下面是一些示例代码,这是一个反映实际问题域的仿真示例,用于演示不良行为。

的观点:

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

回答

11

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

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

做到这一点的一种方法是缓存每辆车内的仪表对象,因为我在最近的问题上显示here。然后,而不是在视图,模型和模板中引用car.meter_set.all(),您将执行car.get_meters()或其他任何操作,并且每次都会获得同一组对象以及动态属性。

+0

真棒谢谢你。 – 2009-11-13 13:36:57

1

我想类似的东西在我自己的代码,它看起来

更新:根据丹尼尔的回答更新代码像您遇到的问题发生在read_meter()方法和odomoter()视图中。

metercar您用于迭代查询集的对象已经超出了范围,并且您对其属性所做的更改也随之发生。

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

希望解释清楚。任何不将值保存到数据库的理由?

+0

我不保存数据的原因很简单,它是暂时的。对我来说,存储数据必须在运行中计算并且永远不会在数据库字段中再次引用,这似乎是错误的。请注意,上面的例子是假的,在实际应用中,一些瞬态数据的大小是数百MB。谢谢你的帮助。 – 2009-11-13 23:03:42