Evgenii Legotckoi
Evgenii Legotckoi4. Juli 2018 02:38

Django - Tutorial 034. Wie man eine Suche in mehreren Datenmodellen durchführt

In früheren Artikeln haben wir uns angesehen, wie man eine Website-Suche auf einer Website durchführt. Nämlich:

Aber was ist, wenn Sie mehr als einen Inhaltstyp haben? Sie können Artikel, Kommentare, Foren und Forenbeiträge haben. Wie dann sein?

Wenn Sie alles selbst machen möchten, ohne Bibliotheken von Drittanbietern zu verwenden, müssen Sie alle erforderlichen Modelle durchsuchen und das Ergebnis zusammenführen. Genauso habe ich es auf der Website gemacht.


urls.py

Sie benötigen eine View-Klasse, die die Suchanforderung verarbeitet.

Der wichtige Punkt hier ist, dass wir Pull-Requests verarbeiten, damit Benutzer Links mit Suchergebnissen teilen können.

In die Datei urls.py schreiben wir die Route für die Suche

app_name = 'home'
urlpatterns = [
    path('search/', views.SearchView.as_view(), name='search'),
]

views.py

Nehmen wir an, wir haben mehrere Inhaltstypen:

  • Artikel
  • Kommentar
  • Thema
  • Post

Es ist notwendig, in der Ansicht nach allen Arten von Inhalten zu suchen und diese zu einem QuerySet zusammenzufassen und für die Paginierung und Auslieferung vorzubereiten

from itertools import chain

from django.shortcuts import render
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.views import View

from .models import Article, Comment, Topic, Post


class ESearchView(View):
    template_name = 'search/index.html'

    def get(self, request, *args, **kwargs):
        context = {}

        q = request.GET.get('q')
        if q:
            query_sets = []  # Total QuerySet

            # Searching for all models
            query_sets.append(Article.objects.search(query=q))
            query_sets.append(Comment.objects.search(query=q))
            query_sets.append(Topic.objects.search(query=q))
            query_sets.append(Post.objects.search(query=q))

            # and combine results
            final_set = list(chain(*query_sets))
            final_set.sort(key=lambda x: x.pub_date, reverse=True)  # Sorting

            context['last_question'] = '?q=%s' % q

            current_page = Paginator(final_set, 10)

            page = request.GET.get('page')
            try:
                context['object_list'] = current_page.page(page)
            except PageNotAnInteger:
                context['object_list'] = current_page.page(1)
            except EmptyPage:
                context['object_list'] = current_page.page(current_page.num_pages)

        return render(request=request, template_name=self.template_name, context=context)

Die Nuance des obigen Codes besteht darin, dass wir alle Datenmodelle verketten und sie auch nach Datum sortieren. Um dies zu ermöglichen, werden wir die Funktionen der Programmiersprache Python verwenden, nämlich Ententypisierung. Die Sortierung nach Datum wird dadurch ermöglicht, dass alle Datenmodelle ein Veröffentlichungsdatumsfeld mit dem gleichen Namen pub_date haben.

Im Allgemeinen ist dies sehr wichtig, wenn Sie versuchen, Datenmodellfelder für verschiedene Datenmodelle gleich zu benennen. Dadurch können Sie bei der Entwicklung einer Django-Site sehr flexibel sein und Ententypisierung verwenden, um Vorlagen für die Anzeige von Daten zu schreiben, die nicht von einem bestimmten Datentyp abhängen, sondern von der Schnittstelle, die Ihre Datenmodelle unterstützen.

Interessant ist auch, dass die Objekte aller Datenmodelle, die in diesem Code dargestellt werden, dieselbe Lookup-Methode haben. Diese Methode ist nicht Standard. Um es zu implementieren, müssen Sie einen eigenen Modellmanager schreiben und ihn dem Bereich der Objekte zuweisen.

Artikelmanager

Stellen Sie sich einen typischen Artikelmanager vor. Wir erben es vom Basismodellmanager und definieren die Suchmethode mithilfe der Suchlogik. Ebenso müssen Sie diese Methode für alle Modelle registrieren, in denen die Suche durchgeführt wird.

from django.db import models
from django.db.models import Q

class ArticleManager(models.Manager):
    use_for_related_fields = True

    def search(self, query=None):
        qs = self.get_queryset()
        if query:
            or_lookup = (Q(title__icontains=query) | Q(content__icontains=query))
            qs = qs.filter(or_lookup)

        return qs

Installieren eines Managers in einem Modell

class Article(models.Model):
    objects = ArticleManager()

suche/index.html

Und für die Suchvorlage können Sie eine leicht modifizierte Vorlage aus einem der ersten Artikel zur Organisation der Suche verwenden.

{% load bootstrap34%}
{% block page %}
    <h1>Поиск</h1>
    {% if object_list %}
        {% for object in object_list %}
            <div>
                <a href="{{ object.get_absolute_url }}">
                    <h2>{{ object.itle }}</h2>
                </a>
                {{ object.content|safe }}
                <p><a class="btn btn-default btn-sm" href="{{ object.get_absolute_url }}">Читать далее</a></p>
            </div>
        {% endfor %}
        {% bootstrap_pagination object_list url=last_question %}
    {% else %}
        <p>Не найдено публикаций по вашему запросу<br>Попробуйте повторить запрос с другой формулировкой</p>
    {% endif %}
{% endblock %}

Ich verwende in diesem Fall speziell den anonymisierten Objektparameter, der keinen Hinweis auf einen bestimmten Inhaltstyp enthält, sodass klar war, dass jedes Inhaltsobjekt vorhanden sein könnte. Die Hauptsache ist, dass alle Methoden für alle Modelle implementiert sind, die in dieser Vorlage verwendet werden.

Рекомендуємо хостинг TIMEWEB
Рекомендуємо хостинг TIMEWEB
Stabiles Hosting des sozialen Netzwerks EVILEG. Wir empfehlen VDS-Hosting für Django-Projekte.

Magst du es? In sozialen Netzwerken teilen!

g
  • 9. Juli 2018 05:00

Исправьте код в views.py, пропущен импорт chain из itertools.

Evgenii Legotckoi
  • 10. Juli 2018 12:49

Спасибо! Исправил.

S
  • 13. Mai 2020 06:36

Добрый день, небольшая проблемка с пагинацией, отображается информация только на первой странице при переходе на другую - пустая страница, views, models.Manager, шаблон, все сделал как в данном уроке, подставил только свои значения. Подскажите, в чем может быть проблема?
Спасибо.

Evgenii Legotckoi
  • 13. Mai 2020 06:41

Добрый день.
Покажите ваш код... Невозможно понять, где вы допустили ошибку, не видя, что вы делали.
Когда будете добавлять код в сообщение, используйте диалог вставки программного кода, одна из кнопок в тулбаре редактора комментариев. Тогда будет добавлена соответствующая разметка для блоков кода.

S
  • 13. Mai 2020 07:28
Models, остальные похожие:

class PostManager(models.Manager):
    use_for_related_fields = True
    def search(self, query=None):
        qs = self.get_queryset()
        if query:
            or_lookup = (Q(title__icontains=query) | Q(text__icontains=query))
            qs = qs.filter(or_lookup)
        return qs

class Post(models.Model):
    title = models.CharField(max_length=40, verbose_name='Название')
    text = RichTextUploadingField(blank=True, null=True, verbose_name='Текст')
    image = models.ImageField(blank=True, upload_to=get_timestamp_path, verbose_name='Изображение')
    is_active = models.BooleanField(default=True, db_index=True, verbose_name='Выводить в списке?')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='Создано')
    published_date = models.DateTimeField(blank=True, null=True, verbose_name='Опубликовано')
    slug = models.SlugField(max_length=255, blank=True)
    objects = PostManager()

    def delete(self, *args, **kwargs):
        for ap in self.postImage_set.all():
            ap.delete()
        super().delete(*args, **kwargs)

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return '/post_list/%s/' % self.pk

    class Meta:
        verbose_name_plural = 'Публикации'
        verbose_name = 'Публикация'


Views:
class SearchView(View):
    template_name = 'main/search.html'

    def get(self, request, *args, **kwargs):
        context = {}

        q = request.GET.get('q')
        if q:
            query_sets = []  # Общий QuerySet

            # Ищем по всем моделям
            query_sets.append(Bb.objects.search(query=q))
            query_sets.append(Article.objects.search(query=q))
            query_sets.append(Post.objects.search(query=q))


            # и объединяем выдачу
            final_set = list(chain(*query_sets))
            final_set.sort(key=lambda x: x.created_at, reverse=True)  # Выполняем сортировку

            context['last_question'] = '?q=%s' % query_sets

            paginator = Paginator( final_set, 2)

            page = request.GET.get('page')

            try:
                context['object_list'] = paginator.page(page)
            except PageNotAnInteger:
                context['object_list'] = paginator.page(1)
            except EmptyPage:
                context['object_list'] = paginator.page(paginator.num_pages)
        return render(request=request, template_name=self.template_name, context=context )




Шаблон:
{% extends "layout/basic.html" %}
{% load thumbnail %}
{% load static %}
{% load bootstrap4 %}

{% block content %}

 <h1>Поиск</h1>
    {% if object_list %}
        {% for object in object_list %}

            <div>
                <a href="{{ object.get_absolute_url }}">
                    <h2>{{ object.title }}</h2>
                </a>
                {{ object.text|safe }}
                <p><a class="btn btn-default btn-sm" href="{{ object.get_absolute_url }}">Читать далее</a></p>
            </div>

        {% endfor %}

        {% bootstrap_pagination object_list  url=last_question %}

    {% else %}
        <p>Не найдено публикаций по вашему запросу<br>Попробуйте повторить запрос с другой формулировкой</p>
    {% endif %}
{% endblock %}


urls:
path('search/', views.SearchView.as_view(), name='search'),


кнопка:

<form class="navbar-form navbar-left m-2 p-4" action="{% url 'main:search' %}">
            <div class="input-group">
                <input id="search" name="q" type="text" class="form-control" placeholder="Найти">
                <div class="input-group-btn">
                    <button class="btn btn-success" type="submit">
                        <i class="fas fa-search"></i>
                    </button>
                </div>
            </div>
        </form>
Evgenii Legotckoi
  • 13. Mai 2020 15:50

Опечатка в статье. Напишите так

context['last_question'] = '?q=%s' % q
Evgenii Legotckoi
  • 13. Mai 2020 16:13
  • (bearbeitet)

И на старуху бывает проруха. Спасибо за вопрос.
Если бы не просмотрел ваш код, то скорее всего не заметил бы опечатку

S
  • 13. Mai 2020 16:18

Спасибо, Евгений, все заработало. Хороший сайт.

Evgenii Legotckoi
  • 13. Mai 2020 16:26

Спасибо за отзыв. Не стесняйтесь задавать вопросы. Только у меня убедительная просьба, все вопросы или на форуме сайта, или в комментариях к статьям. Чтобы потом у других пользователей было больше шансов найти ответ. Также на форуме присутствуют и другие опытные специалисты, они вполне могут тоже что-то подсказать, так что шансы получить ответ будут выше.

S
  • 14. Mai 2020 12:58

Здравствуйте, небольшой вопрос,
при добавлении формы пагинатора левая крайняя кнопка назад "<<" постоянно смещена вверх, относительно ряда цифр, не могу ее никак выровнять, в чем может быть проблема?
{% bootstrap_pagination object_list url=last_question %}
Хотел установить пагинатор прописав код, но немного запутался с адресами между страниц.
Подскажите, как правильно прописать адреса.
Спасибо.

<ul class="pagination">
            {% if object_list.has_previous %}
            <li class="page-item"><a class="page-link" href="?q={{ object_list.previous_page_number }}">&lt;</a></li>
            <li class="page-item"><a class="page-link" href="?q={{ object_list.previous_page_number }}">{{ object_list.previous_page_number }}</a></li>
            {% endif %}

            <li class="page-item active"><a class="page-link" href="?q={{ object_list.number }}">{{ object_list.number }}</a>
            </li>

            {% if object_list.has_next %}
            <li class="page-item"><a class="page-link" href="?q={{ object_list.next_page_number }}">{{ object_list.next_page_number }}</a></li>
            <li class="page-item"><a class="page-link" href="?q={{ object_list.next_page_number }}">&gt;</a></li>
            {% endif %}
        </ul>
Evgenii Legotckoi
  • 15. Mai 2020 03:08

У вас есть ещё какие-то кастомные стили, которые могут влиять на "<<", поскольку у меня такой проблемы не наблюдалось.

Ну по идее, это как-то тако должно выглядеть

href="?q={{q}}&page={{ object_list.previous_page_number }}"
S
  • 15. Mai 2020 05:03

Добрый день, Евгений,
сработал вот такой вариант:
href="?q={{ question }}&page={{ object_list.previous_page_number }}"
и во views добавил:
context['question'] = q
По поводу формы пагинатора, пробовал отключать все дополнительные стили, все также вылезает ошибка. Подумаю еще над этим, главное что есть рабочий вариант.
Спасибо за подсказку.

Kommentare

Nur autorisierte Benutzer können Kommentare posten.
Bitte Anmelden oder Registrieren
Letzte Kommentare
ИМ
Игорь Максимов5. Oktober 2024 14:51
Django – Lektion 064. So schreiben Sie eine Python-Markdown-Erweiterung Приветствую Евгений! У меня вопрос. Можно ли вставлять свои классы в разметку редактора markdown? Допустим имея стандартную разметку: <ul> <li></li> <li></l…
d
dblas55. Juli 2024 18:02
QML - Lektion 016. SQLite-Datenbank und das Arbeiten damit in QML Qt Здравствуйте, возникает такая проблема (я новичок): ApplicationWindow неизвестный элемент. (М300) для TextField и Button аналогично. Могу предположить, что из-за более новой верси…
k
kmssr9. Februar 2024 02:43
Qt Linux - Lektion 001. Autorun Qt-Anwendung unter Linux как сделать автозапуск для флэтпака, который не даёт создавать файлы в ~/.config - вот это вопрос ))
Qt WinAPI - Lektion 007. Arbeiten mit ICMP-Ping in Qt Без строки #include <QRegularExpressionValidator> в заголовочном файле не работает валидатор.
EVA
EVA25. Dezember 2023 18:30
Boost - statisches Verknüpfen im CMake-Projekt unter Windows Ошибка LNK1104 часто возникает, когда компоновщик не может найти или открыть файл библиотеки. В вашем случае, это файл libboost_locale-vc142-mt-gd-x64-1_74.lib из библиотеки Boost для C+…
Jetzt im Forum diskutieren
J
JacobFib17. Oktober 2024 10:27
добавить qlineseries в функции Пользователь может получить любые разъяснения по интересующим вопросам, касающимся обработки его персональных данных, обратившись к Оператору с помощью электронной почты https://topdecorpro.ru…
JW
Jhon Wick1. Oktober 2024 22:52
Indian Food Restaurant In Columbus OH| Layla’s Kitchen Indian Restaurant If you're looking for a truly authentic https://www.laylaskitchenrestaurantohio.com/ , Layla’s Kitchen Indian Restaurant is your go-to destination. Located at 6152 Cleveland Ave, Colu…
КГ
Кирилл Гусарев27. September 2024 16:09
Не запускается программа на Qt: точка входа в процедуру не найдена в библиотеке DLL Написал программу на C++ Qt в Qt Creator, сбилдил Release с помощью MinGW 64-bit, бинарнику напихал dll-ки с помощью windeployqt.exe. При попытке запуска моей сбилженной программы выдаёт три оши…
F
Fynjy22. Juli 2024 11:15
при создании qml проекта Kits есть но недоступны для выбора Поставил Qt Creator 11.0.2. Qt 6.4.3 При создании проекта Qml не могу выбрать Kits, они все недоступны, хотя настроены и при создании обычного Qt Widget приложения их можно выбрать. В чем может …

Folgen Sie uns in sozialen Netzwerken