Django:不同模型下的全局搜索-不会出现Post结果


我希望你身体健康。我在不同的应用程序中创建了一个具有多个模型的全局搜索。

张贴模型属于Nutriscore文件夹

UserProfile模型属于用户文件夹

如果我使用一个涵盖Post和UserProfile搜索的词,例如Main courses。我得到了一些UserProfile的搜索结果(3个结果(,但Post没有出现任何结果(而count给了我6个结果(。所以我不知道代码出了什么问题?

user/views.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


user/uls.py

path('search/', GlobalSearchView.as_view(),name="global_search"),

user/templates/global_search.html我得到了一个用css显示/折叠功能的菜单。

<div class="row" id="collapseNutriscore">
{% for object in object_list %}
{% with object|class_name as klass %}
{% if klass == 'Post' %}

{{ object.title }}

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

<br>
<br>

<div class="row collapse" id="collapseCooker">
{% for object in object_list %}
{% with object|class_name as klass %}
{% if klass == 'UserProfile' %}

{{ object.user.username }}

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

免责声明:这个答案可能不适合OP的视图或模型,但它将与示例一起正常工作。

为了清楚起见,我假设我们有两个模型,MusicianAlbum,它们位于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视图中以获得结果。

为此,我们将SearchMixin类继承到ListView作为

# 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类将搜索结果添加到名为search_result的上下文变量中,并且该类具有另一个变量has_search_result(search_result.has_search_result(;任何匹配"。

此外,每个QuerySet都可以由单独的变量访问。变量的格式为,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

我认为最好把所有东西都像这样放在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变量

您有3个返回语句,因此只执行上的第一个:


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

您应该只保留return results

如果您真的想返回这3项,请以元组形式返回:

return qa, qn, results

最新更新