[英]Search in multiple models in Django
I have many different models in Django and I want to search for a keyword in all of them.我在 Django 中有许多不同的型号,我想在所有型号中搜索关键字。 For example, if you searched "blah", I want to show all of the products with "blah", all of the invoices with "blah", and finally all of the other models with "blah".例如,如果您搜索“blah”,我想显示所有带有“blah”的产品,所有带有“blah”的发票,最后是所有其他带有“blah”的型号。
I can develop a view and search in all of the models separately, but it's not a good idea.我可以单独开发一个视图并在所有模型中进行搜索,但这不是一个好主意。
What is the best practice for this situation?这种情况的最佳做法是什么?
I've run into this situation a few times and one solution is to use model managers, and create distinct search methods for single and multi-word queries.我遇到过几次这种情况,一种解决方案是使用 model 管理器,并为单词和多词查询创建不同的搜索方法。 Take the following example models below: Each has its own custom Model Manager, with two separate query methods.以下面的示例模型为例: 每个模型都有自己的自定义 Model Manager,具有两种独立的查询方法。 search
will query single-word searches against all fields, while search_and
will query each word in a list of search words, using the reduce
function. Both use Q
objects to accomplish multi-field lookups. search
将针对所有字段查询单词搜索,而search_and
将查询搜索词列表中的每个词,使用reduce
function。两者都使用Q
对象来完成多字段查找。
from functools import reduce
from django.db.models import Q
class ProductQuerySet(models.QuerySet):
def search(self, query=None):
qs = self
if query is not None:
or_lookup = (Q(product_name__icontains=query) |
Q(description__icontains=query) |
Q(category__icontains=query))
qs = qs.filter(or_lookup, active=True, released=True).distinct()
return qs
def search_and(self, query=None):
qs = self
if query is not None:
or_lookup = reduce(lambda x, y: x & y, [(Q(product_name__icontains=word) |
Q(description__icontains=word) |
Q(category__icontains=word)) for word in query])
qs = qs.filter(or_lookup, active=True, released=True).distinct()
return qs
class ProductManager(models.Manager):
def get_queryset(self):
return ProductQuerySet(self.model, using=self._db)
def search(self, query=None):
return self.get_queryset().search(query=query)
def search_and(self, query=None):
return self.get_queryset().search_and(query=query)
class Product(models.Model):
product_name = models.CharField(max_length=200)
description = models.CharField(max_length=240, blank=True, null=True)
category = models.CharField(max_length=100, choices=CATEGORY)
objects = ProductManager()
def __str__(self):
return self.product_name
class ProfileQuerySet(models.QuerySet):
def search(self, query=None):
qs = self
if query is not None:
or_lookup = (Q(full_name__icontains=query) |
Q(job_title__icontains=query) |
Q(function__icontains=query))
qs = qs.filter(or_lookup, active=True, released=True).distinct()
return qs
def search_and(self, query=None):
qs = self
if query is not None:
or_lookup = reduce(lambda x, y: x & y, [(Q(full_name__icontains=word) |
Q(job_title__icontains=word) |
Q(function__icontains=word)) for word in query])
qs = qs.filter(or_lookup, active=True, released=True).distinct()
return qs
class ProfileManager(models.Manager):
def get_queryset(self):
return ProfileQuerySet(self.model, using=self._db)
def search(self, query=None):
return self.get_queryset().search(query=query)
def search_and(self, query=None):
return self.get_queryset().search_and(query=query)
class Profile(models.Model):
full_name = models.CharField(max_length=200)
job_title = models.CharField(max_length=240, blank=True, null=True)
function = models.CharField(max_length=100, choices=FUNCTION)
objects = ProfileManager()
def __str__(self):
return self.full_name
Now to use these in a search view, which you can point at as many model managers as you like, which can query as many fields as you like.现在在搜索视图中使用这些,您可以根据需要指向任意多个 model 经理,它可以查询任意多个字段。 Using the example models above, here's a sample view, below.使用上面的示例模型,下面是一个示例视图。 It passes the search term or terms to the appropriate model manager method, based on the count of terms (either 1 or more than 1).它根据术语数(1 个或多于 1 个)将一个或多个搜索术语传递给相应的 model 管理器方法。
def application_search(request):
data = dict()
if 'query' in request.GET:
query_list = request.GET.get("query", None).split()
if query_list:
try:
if len(query_list) > 1:
products = Product.objects.search_and(query=query_list)
profiles = Profile.objects.search_and(query=query_list)
else:
products = Product.objects.search(query=query_list[0])
profiles = Profile.objects.search(query=query_list[0])
except:
# Throw exception or log error here
try:
queryset_chain = chain(products, profiles) # combines querysets into one
results = sorted(queryset_chain, key=lambda instance: instance.id, reverse=True) #sorts results by ID
except:
results = None
data['results'] = render_to_string('pages/my_search_result_page.html', {'results': results})
return JsonResponse(data)
The query
in this view is actually being passed to the backend via AJAX, but you may do it differently based on your needs and template design.此视图中的query
实际上是通过 AJAX 传递到后端的,但是您可以根据您的需要和模板设计进行不同的操作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.