获取相关对象

时间:2015-10-14 07:12:40

标签: python django django-models django-templates

我有一个普通用户,他有一些测量值,每个测量值都有一个或多个标记结果:

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中的相关模型?

归结起来的是,尽管上面的django模板完全可以使用get_users_with_tags返回的值,但这些结果不可用(不可序列化,因为它们对于django rest API,它不是真正的QuerySet?)。

我想改进这个功能,以便产生django模板和django rest API框架都可以使用的结果。

1 个答案:

答案 0 :(得分: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)

在这里,您将获得一个标签列表