【问题标题】:Best way to show data from multiple tables显示来自多个表的数据的最佳方式
【发布时间】:2016-11-28 06:38:24
【问题描述】:

我有一个反复出现的问题,我似乎无法充分解决。我的网站类似于工作网站,人们可以在其中发布工作(以及其中的详细信息),其他人可以为它们添加书签。显然,每个作业都可以被多个查看者收藏。

这是model.py:

class Job(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=150)
    description = models.CharField(max_length=5000)


class Bookmark(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    job = models.ForeignKey(Job, on_delete=models.CASCADE)

我的目的是在主页上显示所有工作,这意味着来自多个用户的多个工作。其他用户可以点击一个小书签图标来添加书签,或者取消添加书签。像这样:

这是 view.py:

def index(request):
    context = {}
    populateContext(request, context)

    jobs = Job.objects.all().order_by('-id')

    context = {'jobs': jobs}


    return render(request, 'templates/index.html', context)

我觉得很简单,这是我的意图。以下是模板的外观:

{% for job in jobs %}
    <div>
        {{ job.title }}<br>
        {{ job.description }}<br>
        by {{ job.user.username }}
    </div>
{% endfor %}

我的问题是,如何显示特定于每个用户的书签状态?这是我目前的解决方案:

{% for job in jobs %}
    <div>
        {{ job.title }}<br>
        {{ job.description }}<br>
        by {{ job.user.username }}<br>

        {% for watch in job.bookmark_set.all %}
            {% if watch.user_id = request.user.id %}
                You have bookmarked this! <a>Unbookmark!</a>
            {% endif %}
        {% endfor %}

        <a>Bookmark</a>
    </div>
{% endfor %}

“取消书签!”顺便说一下,链接将定位在“书签”链接上。上面的解决方案就是这样工作的,因为书签表将包含零个或多个特定作业的书签,但在多个用户下。我将在 view.py 中处理这个问题,只过滤登录用户所做的工作。但我无法具体过滤哪个作业,因为 index.html 将显示它们全部。例如,我可以把它吐出来……:

bookmarked = Bookmark.objects.all().filter(user_id=request.user.id)

...这将列出登录用户所做的不同作业的所有书签。我仍然需要在模板中对其进行过滤,以便每个项目与每个书签匹配,我知道这是不可能的。

无论如何,我认为这是非常低效的。所以我想知道是否有更简单的方法来处理这个问题?最好这样工作:

{% for job in jobs %}
    <div>
        {{ job.title }}<br>
        {{ job.description }}<br>
        by {{ job.user.username }}<br>

        {% if job.id = bookmark.job.id %}
            You have bookmarked this! <a>Unbookmark!</a>
        {% else %}
            <a>Bookmark</a>
        {% endif %}
    </div>
{% endfor %}

谢谢!

【问题讨论】:

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


    【解决方案1】:

    您可以使用conditional expressions 使用此信息注释您的jobs。在视图中:

    from django.db.models import Case, IntegerField, Sum, When
    
    jobs = Job.objects.annotate(
               is_bookmarked=Sum(Case(
                   When(bookmark__user=request.user, then=1), 
                   default=0, output_field=IntegerField()
               ))).order_by('-id')
    

    每个job 现在都有一个is_bookmarked 属性,即1(用户已为作业添加书签)或0。在您的模板中:

    {% for job in jobs %}
        <div>
            {% if job.is_bookmarked %}
                You have bookmarked this! <a>Unbookmark!</a>
            {% else %}
                <a>Bookmark</a>
            {% endif %}
        </div>
    {% endfor %}
    

    为了完整起见,您想到的另一种方法也可以使用(尽管效率低于上述方法)。在视图中:

    jobs = Job.objects.all().order_by('-id')
    # Get a list of all Job IDs bookmarked by this user
    user_bookmarks = Bookmark.objects.filter(user_id=request.user.id)\
                                     .values_list('job__id', flat=True)
    

    在模板中:

    {% for job in jobs %}
        <div>  
            {% if job.id in user_bookmarks %}
                You have bookmarked this! <a>Unbookmark!</a>
            {% else %}
                <a>Bookmark</a>
            {% endif %}
        </div>
    {% endfor %}
    

    这两种方法的逻辑几乎相同 - 不同之处在于第一种方法是在数据库级别执行此操作,通常效率更高。

    【讨论】:

    • 非常感谢您的详细解决方案和解释!第二种方法效果很好。但是,第一个没有。如果作业已添加书签,则会显示该作业两次(一次显示为未添加书签,另一次显示为已添加书签)。另外,这两种方法之间是否存在速度差异?
    • 嗯,让我尝试修复第一种方法,使其工作......它更有效,因为它只需要数据库查询而不需要列表过滤。
    • @Bob 我已经编辑了第一个选项以使其工作。它比我最初提出的要复杂一点,但应该可行。
    猜你喜欢
    • 2016-12-30
    • 1970-01-01
    • 1970-01-01
    • 2021-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-28
    相关资源
    最近更新 更多