【问题标题】:Creating a context object with values from several related models使用来自多个相关模型的值创建上下文对象
【发布时间】:2018-08-25 04:30:33
【问题描述】:

我似乎无法弄清楚这一点。我的视图接受Team.id 的参数,并且我想返回一个上下文对象,其中每个User 对象在User.profile.team 中具有特定值,并且关联日期来自Reports。我觉得我开始在正确的轨道上,但我错过了一些东西。我的模板的输出包含我试图获取的所有数据,但不是以一种可以逻辑显示的方式。

基本上,我使用类似于以下的模型:

class Reports(models.Model):
    user = models.ForeignKey(User, null=True, on_delete=models.PROTECT)
    product = models.CharField(max_length=15)
    apps_activated = models.IntegerField(blank=True, null=True)
    prem_submitted = models.DecimalField(max_digits=30, decimal_places=2)

class Team(models.Model):
    name = models.CharField(max_length=255)
    leader = models.ForeignKey(User,on_delete=models.PROTECT)

扩展用户配置文件:

class Profile(models.Model):
    COORDINATOR = 1
    LEADER = 2
    ADMIN = 3
    ROLE_CHOICES = (
        (COORDINATOR, 'Coordinator'),
        (LEADER, 'Leader'),
        (ADMIN, 'Admin'),
    )
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
    role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)

我认为返回所需数据的最接近的是以下内容:

team = 1
team_name = Team.objects.get(id=team)
team_users = User.objects.filter(profile__team=team).all()
team_stats = []

for user in team_users:
    team_stats.append(Reports.objects.filter(user_id=user.id))

使用看起来像这样的模板:

{% block content %}
<h1>{{ team }}</h1>
<ul>
{% for user in team_users %}
    <li><a href="/reports/user/{{ user.id }}">{{ user.first_name }} {{ user.last_name }}</a></li>
{% endfor %}
</ul>

<ul>
{% for stat in team_stats %}
    <li>
    {% for line in stat %}
        {{ line.product }} {{ line.type }} #etc #etc
        {% endfor %}
    </li>
{% endfor %}
</ul>

{% endblock %}

我以为我对prefetch_related() 有所了解,但想不通。理想情况下,我只需要将一个上下文对象返回到我的模板。

编辑:

如果它更清楚,这个查询会返回我试图传递给模板的结果:

select auth_user.first_name, auth_user.last_name, r.product, r.apps_activated, r.prem_submitted, r.conversion_percentage, r.type
from auth_user
join home_profile
on auth_user.id = home_profile.user_id
join reports_reports as r
on auth_user.id = r.user_id
where home_profile.team_id = 1

返回如下所示的行:

first_name-last_name-product-apps_activated-prem_submitted-conversion_rate-type    
user_1-user_1_last-product_1-693-139764.00-53.86-type1
user_1-user_1_last-product_2-74-27400.10-0.00-type1
user_1-user_1_last-product_3-102-19782.00-47.00-type2
user_2-user_2_last-product_1-7-2437.70-0.00-type2
user_2-user_2_last-product_2-52-10608.00-42.54-type3
user_2-user_2_last-product_3-260.40-0.00-type3

【问题讨论】:

  • 你好安德鲁。是否可以概述您在模板上的第二个 for 循环中看到的内容...
  • 它从reports 返回相关值,其顺序与users 在第一个循环中列出的顺序相同。例如,在我的数据库中,reports 可能有一行看起来像:id - product - apps_activated - prem_submitted - type - user_id,并且每个都在特定用户的嵌套循环中的 &lt;li&gt; 标记之间返回。如果有帮助,我可以从我的数据库和模板的实际输出中截取一行?
  • 所以它工作正常,但你更喜欢更整洁的东西。是否可以在模板中大致勾勒出您想要的内容...?
  • 对!我基本上希望能够结合这两个结果,所以我可以将reportsuser 相关联,如果这有意义吗?此外,我在帖子底部添加了一个 SQL 查询,说明理想情况下我希望如何返回对象。当我运行这个查询时,它会返回我希望它们在模板中组织的结果。
  • 感觉就像你想要一个select_related() 查询 - 我会尝试添加一些指导作为答案,但它可能需要一些修改!

标签: python django django-models django-templates django-views


【解决方案1】:

潜在解决方案 1

所以,我会做以下事情。在 Reports 模型中更改 user 以显式链接到 Profile(反过来,链接到用户)

class Reports(models.Model):
    profile = models.ForeignKey(Profile, null=True, on_delete=models.PROTECT)
    product = models.CharField(max_length=15)
    apps_activated = models.IntegerField(blank=True, null=True)
    prem_submitted = models.DecimalField(max_digits=30, decimal_places=2)

您可能正在寻找类似select_related() 查询的内容,为个人资料添加额外的ForeignKey 字段:

reports = Reports.objects.select_related('profile')

您可以通过str(reports.query) 检查生成的 SQL,这应该会导致您在问题中概述的内容。

然后将返回的游标值转换为适当的 ORM 模型实例,这样当您遍历这些 reports 时,您可以通过它们自己的对象访问相关表的值。但是,这些沿预选前向关系的访问不会导致额外的数据库命中:

{% for report in reports %}
     {{ report.profile.user.username }}
     {{ report.product }}
     # ...
{% endfor %}

让我知道你的进展情况,如果我们能集思广益,找到更合适的解决方案,如果这个不是。

潜在解决方案 2

也许另一种解决方案,也许是两者中最简单的一种,是在您的 Profile 模型中与 Reports 建立多对多关系:

class Profile(models.Model):
    COORDINATOR = 1
    LEADER = 2
    ADMIN = 3
    ROLE_CHOICES = (
        (COORDINATOR, 'Coordinator'),
        (LEADER, 'Leader'),
        (ADMIN, 'Admin'),
    )
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    team = models.ForeignKey(Team, on_delete=models.PROTECT,null=True)
    role = models.PositiveSmallIntegerField(choices=ROLE_CHOICES, null=True, blank=True)
    reports = models.ManyToManyField(Reports, ...)

那么你应该可以在模板中循环users

{% for user in users %}
    {% for report in user.reports.all %}
        {{ report.product }}
    {% endfor %}
{% endfor %}

【讨论】:

  • 感谢您的详细回复!将进行更改并报告!
  • 别担心,现在是早上 6 点,所以会睡一会,但首先会办理入住手续。我还有另一个潜在的解决方案,您也可以使用它 - 但随着时间的推移,它可能会使您的数据库膨胀。
  • 解决方案 1 完全符合我的需要!还有一个问题:我尝试将.filter() 添加到Reports.objects.select_related('profile') 以通过team_id 进行过滤。我可以通过将'team': {'name': team_name, 'team_id':team }, 添加到我的上下文对象并在template 中的for 语句中添加if 语句来显示团队成员,例如if team_id = report.profile.team_id,但是有没有办法在view 中过滤它?
  • 如果您尝试 Reports.object.select_related('profile').select_related('team') 会发生什么?
  • 我收到以下错误:Invalid field name(s) given in select_related: 'team'. Choices are: profile。也试过select_related(__profile_team_id)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 2021-12-09
  • 2015-03-24
  • 1970-01-01
  • 2016-01-22
  • 2016-09-11
相关资源
最近更新 更多