为了账号安全,请及时绑定邮箱和手机立即绑定

Django:不同模型中的全局搜索 - 发布结果不出现

Django:不同模型中的全局搜索 - 发布结果不出现

温温酱 2023-06-27 13:58:16
我希望你很好。我在不同的应用程序中创建了一个具有多个模型的全局搜索。帖子模型属于 Nutriscore 文件夹UserProfile 模型属于 User 文件夹如果我使用一个涵盖帖子和用户配置文件搜索的单词,例如主课程。我得到了 UserProfile 的一些搜索结果(3 个结果),但 Post 没有出现任何内容(而count给了我 6 个结果)。所以我不知道代码有什么问题?用户/视图.py# global search class GlobalSearchView(ListView):    template_name = 'search_global.html'    count = 0    countnutri = 0        def get_context_data(self, *args, **kwargs):        context = super().get_context_data(*args, **kwargs)        context['count'] = self.count or 0        context['countnutri'] = self.countnutri or 0        context['query'] = self.request.GET.get('q')        return context        def get_queryset(self): # new        query = self.request.GET.get('q', None)        if query is not None:            nutriscore = Post.objects.filter(                Q(title__icontains=query) | Q(slug__icontains=query) | Q(typederepas__name__icontains=query) | Q(prixrepas__name__icontains=query) | Q(vitesserepas__name__icontains=query) | Q(force__name__icontains=query) | Q(bienfaitrepas__name__icontains=query)            ).distinct()            user = UserProfile.objects.filter(                Q(pays__icontains=query) | Q(town__icontains=query) | Q(user__username__icontains=query) | Q(mealtrend__name__icontains=query) | Q(pricetrend__name__icontains=query) | Q(speedtrend__name__icontains=query)| Q(strengthtrend__name__icontains=query) | Q(wellnesstrend__name__icontains=query)            ).distinct()            results = chain(nutriscore,user)                        qs = sorted(user,                         key=lambda instance: instance.pk,                         reverse=True)            self.count = len(qs)                        qn = sorted(nutriscore,                         key=lambda instance: instance.pk,                         reverse=True)            self.countnutri = len(qn)                        return qs            return qn            return results
查看完整描述

3 回答

?
梵蒂冈之花

TA贡献1900条经验 获得超5个赞

免责声明:这个答案可能不适合OP的观点或模型,但是,它将按原样与示例一起工作。


为了清楚起见,我假设我们有两个模型,Musician 它们Album位于sample应用程序中。


# sample/models.py


from django.db import models



class Musician(models.Model):

    name = models.CharField(max_length=50)


    def __str__(self):

        return f'{self.name}'



class Album(models.Model):

    artist = models.ForeignKey(Musician, on_delete=models.CASCADE)

    name = models.CharField(max_length=100)

    description = models.TextField()


    def __str__(self):

        return f'{self.name} : {self.artist}'

然后,我们必须创建一个mixin类,以获得更好的OOP 体验以及跨多个视图的可扩展性。


#sample/mixins.py


from django.apps import apps

from django.db.models import Q

from functools import reduce

from operator import or_



class SearchMixin:

    search_keyword_arg = 'q'

    search_settings = {}

    lookup_expr = 'icontains'


    def get_search_term(self):

        return self.request.GET.get(self.search_keyword_arg)


    def build_search_query(self, model_ref, term):

        return reduce(or_, [Q(**{f'{field}__{self.lookup_expr}': term}) for field in self.search_settings[model_ref]])


    def get_search_results(self):

        has_search_result = False

        search_term = self.get_search_term()

        if not search_term:

            return {'has_search_result': has_search_result}


        results = {}


        for model_ref, fields in self.search_settings.items():

            app_name, model_str = model_ref.split('.')

            ModelKlass = apps.get_model(app_label=app_name, model_name=model_str)

            qs = ModelKlass.objects.filter(self.build_search_query(model_ref, search_term))

            results[model_ref.replace('.', '_').lower()] = qs

            if has_search_result is False and qs.exists():

                has_search_result = True


        results['has_search_result'] = has_search_result

        return results


    def get_context_data(self, **kwargs):

        context = super().get_context_data(**kwargs)

        context['search_result'] = self.get_search_results()

        return context

这个SearchMixin类有我们想要的搜索功能。我们可以将此类添加到任何 Django 视图中来获取结果。


为此,我们将SearchMixinclass 继承为ListViewas,


# sample/views.py


from django.views.generic import TemplateView

from sample.mixins import SearchMixin



class GlobalSearchView(SearchMixin, TemplateView):

    template_name = 'sample/global_search.html'

    search_settings = {

        'sample.Musician': ['name'],

        'sample.Album': ['name', 'description'],

    }

笔记:

  • 我使用了TemplateView适合这种特殊情况的方法。

  • 有一个名为的新类属性search_settings,用于确定搜索字段。

search_settings属性应该如何?

  • 它必须是一个dict(或dict类似的对象)

  • 对象的键dict应该采用以下app_name.ModelClassName格式

  • 的值dict必须是模型字段的可迭代

它在模板中看起来怎么样?
该类SearchMixin将搜索结果添加到名为 and 的上下文变量中,search_result该变量还有另一个变量has_search_resultsearch_result.has_search_result),可用于检查我们是否有“任何匹配”

此外,每个查询集都可以通过单独的变量访问。变量的格式为, app_name<underscore><model_name_in_lower_case>.

例如,对于模型,可以在模板中sample.Musician获取搜索结果(如果有),如下所示:{{ search_result.sample_musician }}

# sample/templates/sample/global_search.html


{% if search_result.has_search_result %}

    <strong>Musician result</strong><br>

    {% for musician in search_result.sample_musician %}<br>

        {{ musician.name }}

    {% endfor %}

    <br><br>

    <strong>Album result</strong><br>

    {% for album in search_result.sample_album %}

        {{ album.name }} -- {{ album.description }}<br>

    {% endfor %}

{% else %}

    No match

{% endif %}

现在,连接视图urls.py并使用查询参数进行搜索,如下所示:


/foo-bar/global-search/?q=xx




查看完整回答
反对 回复 2023-06-27
?
元芳怎么了

TA贡献1798条经验 获得超7个赞

我认为最好将所有内容都get_context_data这样放入:


 # global search 

class GlobalSearchView(ListView):

    template_name = 'search_global.html'

    count = 0

    countnutri = 0

    

    def get_context_data(self, *args, **kwargs):

        context = super().get_context_data(*args, **kwargs)

        context['count'] = self.count or 0

        context['countnutri'] = self.countnutri or 0

        query = self.request.GET.get('q', None)

        context['query'] = query

        if query:

            nutriscore = Post.objects.filter(

                Q(title__icontains=query) | Q(slug__icontains=query) | Q(typederepas__name__icontains=query) | Q(prixrepas__name__icontains=query) | Q(vitesserepas__name__icontains=query) | Q(force__name__icontains=query) | Q(bienfaitrepas__name__icontains=query)

            ).distinct()

            user = UserProfile.objects.filter(

                Q(pays__icontains=query) | Q(town__icontains=query) | Q(user__username__icontains=query) | Q(mealtrend__name__icontains=query) | Q(pricetrend__name__icontains=query) | Q(speedtrend__name__icontains=query)| Q(strengthtrend__name__icontains=query) | Q(wellnesstrend__name__icontains=query)

            ).distinct()

            context['results'] = chain(nutriscore,user)

        return context

results然后您可以在模板中使用变量


查看完整回答
反对 回复 2023-06-27
?
精慕HU

TA贡献1845条经验 获得超8个赞

你有 3 个 return 语句,所以只有第一个会被执行:

            return qs  # return user
            return qn  # return nutriscore / NOT EXECUTED
            return results  # return results / NOT EXECUTED

你应该只保留return results.

如果您确实想返回 3 个项目,请以元组形式返回:

            return qa, qn, results


查看完整回答
反对 回复 2023-06-27
  • 3 回答
  • 0 关注
  • 123 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信