2016-07-25 3 views
1

У меня есть повторяющаяся проблема, которую я, похоже, не могу решить адекватно. Мой сайт сродни рабочему месту, где люди могут размещать задания (и детали внутри), а другие могут добавлять закладки в tthem. Очевидно, что каждая работа может быть отмечена более чем одним зрителем.Лучший способ показать данные из нескольких таблиц

Так вот 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 %} 

«Unbookmark!» Кстати, ссылка будет размещена по ссылке «Закладка». Решение выше работает так, потому что в таблице закладок будет содержаться нуль или более закладок для определенного задания, но у нескольких пользователей. Я бы обработал это в 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 %} 

Спасибо!

ответ

3

Вы можете использовать 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 %} 

Оба этот подход делает в значительной степени той же логику - с той разницей, что первый один делает это на уровне базы данных, которая обычно более эффективным ,

+0

Элегантное решение! – gglasses

+0

Большое спасибо за подробное решение и объяснение! Второй подход работает отлично. Тем не менее, первый не делает. Если задание было занесено в закладки, оно дважды отображает задание (один раз, как не закладок, и еще раз как закладок). Кроме того, существует ли разница в скорости между обоими подходами? – Bob

+0

Хмм, позвольте мне попробовать и исправить первый подход, чтобы он работал ... он более эффективен, так как он требует только запроса БД и фильтрации списка. – solarissmoke

Смежные вопросы