2015-10-14 112 views
1

我有一个普通用户,谁拥有一些测量,每一种都可以有一个或多个标签的结果:获取相关对象

class User(AbstractBaseUser): 
    born = models.DateField(null=True, blank=True) 
    ... 

class Measurement(models.Model): 
    user = models.ForeignKey(User, related_name='measurements') 
    ... 

class MeasurementResult(models.Model): 
    measurement = models.ForeignKey(Measurement, related_name='results') 
    tag = models.CharField(max_length=255) 
    value = models.FloatField() 
    ... 

我想获得的所有用户,所有的测量,并与标签所有结果,这样我可以做到以下几点:

{% for user in users %} 
<div class="row"> 
    <div class="col-md-4">{{user.name}}, {{user.age}}</div> 
    <div class="col-md-4">{{user.tags}}</div> 
    <div class="col-md-4"><a href="{% url 'user_detail' user.id %}"> View detail</a></div> 
</div> 
{% endfor %} 

我这里有一些问题,我不知道如何正确解决:

  • user.age:我没有用户的年龄,但是用户出生的日期。我如何将一个从数据库中的天生属性派生的age属性添加到queryset中的用户?
  • user.tags:这比较棘手,因为标签是从给定用户的所有测量结果的所有标签的联合(集合)中获得的。我想我需要为此使用注释,但不知道如何去做。

目前的年龄我已经添加了一个方法,将User型号:

class User(AbstractBaseUser): 
    ... 
    def age(self): 
     '''Returns the age of this user''' 
     if not self.born: 
      return None 
     today = date.today() 
     return today.year - born.year - ((today.month, today.day) < (born.month, born.day)) 

这是添加一个属性到模型的正确,也可能?通过添加一个方法?或者是更好的Django方式这样做吗?

并添加标签,我有一个简单的辅助功能,它增加了一个属性为每个用户:

def get_users_with_tags(skip_user=None): 
    '''Lists all users, adding to each user the measurements results' tags''' 
    users = User.objects.all() 
    if skip_user: 
     users = users.exclude(pk=skip_user.id) 
    for user in users: 
     measurements = Measurement.objects.filter(user=user.id) 
     tags = set() 
     for measurement in measurements: 
      results = MeasurementResult.objects.filter(measurement=measurement) 
      for result in results: 
       tags.add(result.tag) 
     user.tags = ','.join(list(tags)) 
    return users 

但我不喜欢,我要求每个用户的测量,结果为每次测量。有没有办法在单个数据库访问中获得所有这些?我不喜欢user.tags ad-hoc属性添加这样。有没有更好的方法来做到这一点? UserManager也许,但如何访问Model Manager中的相关模型?

什么都可以归结为是,即使由get_users_with_tags返回的值是通过上述Django模板非常有用的,这些相同的结果是不可用的(不可序列,因为他们是不是一个真正的QuerySet?)对一个Django rest API。

我想改进该函数,以便产生可供django模板和django rest API框架使用的结果。

回答

2

我觉得对于这两种情况你可以使用属性装饰器;

class User(AbstractBaseUser): 
born = models.DateField(null=True, blank=True) 
..... 

@property 
def age(self): 
    '''Returns the age of this user''' 
    if not self.born: 
     return None 
    today = date.today() 
    return today.year - born.year - ((today.month, today.day) < (born.month, born.day)) 


@property 
def tags(self): 
    '''Returns the tags of this user''' 
    return MeasurementResult.objects.filter(measurement_id__in=self.measurements.all().values_list('id', flat=True)).distinct().values_list('tag', flat=True) 

在这里,您将获得的标签

+1

甚至不需要成为财产目录,因为Django的模板标签将识别与OP的当前模板代码 – Sayse

+0

事实上的方法;无论如何,这里是属性装饰链接:https://docs.python.org/2/library/functions.html#property – Wtower

+0

@Sayse,我很困惑:我需要声明它作为属性为它在任何工作背景?我的意思是,不仅在模板上下文中,而且在串行器上下文中。 – dangonfast