Как показать ссылки на категорию с помощью Django

Я кодирую проект закладки, чтобы изучить django. Я хочу показывать ссылки, относящиеся к категории. но когда я запускаю цикл for, категории показываются дважды, а дизайн карточек сортируется одна под другой, а не бок о бок. Я думаю, что проблема в файле index.html. Я думаю, что мне нужно использовать команду if, но не знаю как.

index.html

views.py

def index(request):
        bookmark_list = Bookmarks.objects.all()
        return render(request,"index.html",{'bookmark_list':bookmark_list})

models.py

from django.db import models


class Category(models.Model):
        name = models.CharField(max_length=50)
        def __str__(self):
                return self.name

        class Meta:
                db_table = ''
                managed = True
                verbose_name = 'Category'
                verbose_name_plural = 'Categorys'


# Create your models here.

class Bookmarks(models.Model):
    title = models.CharField( max_length=50)
    category = models.ForeignKey(Category, null=True, on_delete=models.DO_NOTHING)
    description = models.TextField(max_length=1000,blank=True)
    link = models.URLField(max_length=400)
    date = models.DateField(auto_now=True)


    def __str__(self):
        return self.title

введите описание изображения здесь

Мне кажется, я вижу две отдельные проблемы:

1. Отображение всех ссылок на закладки на одной карточке, сгруппированных по категориям

Для достижения этой цели можно использовать набор вложенных циклов {% for %}. Сейчас вы передаете bookmarks как объект в шаблон, что хорошо, но затрудняет группировку по категориям. Вы можете получить доступ к связанному набору Bookmarks из каждого Category гораздо проще. Я рекомендую изменить ваш views.py на что-то вроде следующего:

# views.py
#=====================================================================
def index(request):
    categories = Category.objects.all()
    return render(request,"index.html",{'categories':categories})

Затем, внутри вашего шаблона, вы можете установить вложенный цикл следующим образом:

<!--index.html-->
<!--===============================================================-->
{% comment %} The first 'for' loop makes a card with the category's 
name as a heading {% endcomment %}

{% for category in categories %}
<div class="px-6 xl:px-0">
  <div class="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 pb-6 gap-8">
    <div role="cell" class="bg-gray-100">
      <div class="bg-white p-5 rounded-md relative h-full w-full">
        <h1 class="pb-4 text-2xl font-semibold">{{ category.name }}</h1>

{% comment %} The nested 'for' loop goes through each bookmark that is 
related to the category on this card is dedicated to {% endcomment %}

        {% for bookmark in category.bookmark_set.all %}
        <div class="my-5">
          <div class="flex items-center pb-4 dark:border-gray-700 cursor-pointer w-full space-x-3">
            <svg xmlns="http://www.w3.org/2000/svg" width="12.5" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
            </svg>
            <a href="{{bookmark.link}}">
              <h4 class="text-md text-gray-900 dark:text-gray-100" data-bs-toggle="tooltip" data-bs-placement="right" title="{{bookmark.description}}">
                {{bookmark.title}}
              </h4>
            </a>
          </div>
        </div>
        {% endfor %}        

        <a class="hover:text-indigo-500 hover:underline absolute bottom-5 text-sm text-indigo-700 font-bold cursor-pointer flex items-center" href="javascript:void(0)">
          <p>Show All</p>
          <div>
            <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-narrow-right" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="#4338CA" fill="none" stroke-linecap="round" stroke-linejoin="round">
              <path stroke="none" d="M0 0h24v24H0z" fill="none" />
              <line x1="5" y1="12" x2="19" y2="12" />
              <line x1="15" y1="16" x2="19" y2="12" />
              <line x1="15" y1="8" x2="19" y2="12" />
            </svg>
          </div>
        </a>

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

По сути, category.bookmark_set.all использует отношение внешнего ключа для получения отфильтрованного списка всех закладок, относящихся к заданной категории. Документация Django рассказывает больше о том, как это работает здесь: https://docs.djangoproject.com/en/dev/ref/templates/language/#accessing-method-calls

Недостатки

<<<Исходя из вашего

, если у вас есть models.py с нулевым внешним ключом, он не будет отображен, поскольку он не только не принадлежит Bookmark, но и вообще не имеет отношения к Category. Одним из способов решения этой проблемы может быть создание универсального Category под названием "None" или что-то в этом роде. Затем вы можете добавить Category, указывающий на default вашей категории "None" в pk. Это будет означать, что даже если закладка будет создана без явного создания пользователем категории, она будет автоматически отнесена к категории "None".Bookmark.category

Кроме того, поскольку on_delete = models.DO_NOTHING для вашего Bookmark, если вы удалите Category, к которому принадлежит Bookmark, Bookmark не будет отображаться, поскольку он будет иметь отношение к Category, которого не существует. Это может вызвать проблемы с целостностью базы данных. Поскольку ваш Bookmark.category может быть установлен в null, я рекомендую (по крайней мере) установить вместо него on_delete = models.SET_NULL. Если вы решили использовать универсальную категорию "None", я бы установил on_delete = models.SET(<pk>), где <pk> - первичный ключ "None" Category. Если вы решили добавить значение по умолчанию для Bookmark.category, вы также можете установить on_delete = models.SET_DEFAULT. Вы можете прочитать больше об этом в документации Django здесь: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.on_delete

2. Раскладывание карт слева направо

Я не уверен, какой фреймворк вы используете для своих HTML-классов, но причина того, что ваш макет не работает так, как вы хотите, почти наверняка кроется там. Если вы используете Bootstrap, вы можете сделать следующее; если нет, надеюсь, это поможет вам понять, как можно добиться того же самого.

Bootstrap использует 12-колоночную систему сетки для макетов. Вы должны быть в состоянии заставить ваши карточки отображаться слева направо в три колонки, выполнив следующие действия:

<!--index.html-->
<!--===============================================================-->
<div class="container">
  <div class="row">

{% comment %} Add a <div> node INSIDE the first 'for' loop {% endcomment %}
{% for category in categories %}
    <div class="col-4">

      <!--Card code goes here; remember, you don't want 3 'for' loops;
          just 2!-->

      <div class="px-6 xl:px-0">
        <div class="grid sm:grid-cols-1 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-3 pb-6 gap-8">
          <div role="cell" class="bg-gray-100">
            <div class="bg-white p-5 rounded-md relative h-full w-full">
              <h1 class="pb-4 text-2xl font-semibold">
                {{ category.name }}
              </h1>

{% comment %} The nested 'for' loop goes through each bookmark that is 
related to the category on this card is dedicated to {% endcomment %}

              {% for bookmark in category.bookmark_set.all %}
              <div class="my-5">
                <div class="flex items-center pb-4 dark:border-gray-700 cursor-pointer w-full space-x-3">
                  <svg xmlns="http://www.w3.org/2000/svg" width="12.5" height="16" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7" />
                  </svg>
                  <a href="{{bookmark.link}}">
                    <h4 class="text-md text-gray-900 dark:text-gray-100" data-bs-toggle="tooltip" data-bs-placement="right" title="{{bookmark.description}}">
                      {{bookmark.title}}
                    </h4>
                  </a>
                </div>
              </div>
              {% endfor %}        

              <a class="hover:text-indigo-500 hover:underline absolute bottom-5 text-sm text-indigo-700 font-bold cursor-pointer flex items-center" href="javascript:void(0)">
                <p>Show All</p>
                <div>
                  <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-narrow-right" width="16" height="16" viewBox="0 0 24 24" stroke-width="1.5" stroke="#4338CA" fill="none" stroke-linecap="round" stroke-linejoin="round">
                    <path stroke="none" d="M0 0h24v24H0z" fill="none" />
                    <line x1="5" y1="12" x2="19" y2="12" />
                    <line x1="15" y1="16" x2="19" y2="12" />
                    <line x1="15" y1="8" x2="19" y2="12" />
                  </svg>
                </div>
              </a>

            </div>
          </div>
        </div>
      </div>

    </div>
{% endfor %}

  </div>
</div>

Класс col-4 создает колонку шириной 4 из 12 колонок Bootstrap. Это означает, что независимо от ширины экрана на экране всегда будет 3 колонки. Похоже, что вы пытаетесь создать отзывчивую страницу, которая уменьшает количество колонок в зависимости от размера экрана; в документации Bootstrap описано, как это сделать: https://getbootstrap.com/docs/5.1/layout/grid/

Вы не обязаны использовать Bootstrap (и это ни в коем случае не реклама его), просто я лучше всего знаком с тем, как настроить подобный макет с помощью Bootstrap. Похоже, что класс grid на одном из узлов <div> делает что-то похожее на это; любой фреймворк, который вы используете, вероятно, документирует способ принудительной схемы расположения слева направо-вниз (а не сверху-вниз-вправо).

Вернуться на верх