[英]Proper way to do a robust search in Django models via REST framework
I'm writing a web application (DRF + Vue.js) where frontend should have an ability to narrow down GET request results via different filters.我正在编写一个 Web 应用程序(DRF + Vue.js),其中前端应该能够通过不同的过滤器缩小 GET 请求结果的范围。
For example, I have a model like this:例如,我有一个这样的模型:
class Human(models.Model):
first_name = models.CharField(_('first name'), max_length=50, null=True, blank=True)
last_name = models.CharField(_('last name'), max_length=50, null=True, blank=True)
birth_date = models.DateField(_('birth date'), blank=True, null=True)
city = models.ForeignKey('City', on_delete=models.SET_NULL, blank=True, null=True)
phone_number = models.ForeignKey('Contact' on_delete=models.SET_NULL, blank=True, null=True)
@property
def full_name(self):
# Last name + First name
return ' '.join(str(x) for x in (self.last_name, self.first_name) if x)
@property
def is_adult(self):
now = timezone.now()
if self.birth_date:
if now.year - self.birth_date.year - \
((now.month, now.day) < (self.birth_date.month, self.birth_date.day)) >= 18:
return True
return False
Now I have simple CRUD ViewSet where I can use a list of search_fields
to search by all needed fields (in my case that's birth_date
, city
, phone_number
and full_name
/ is_adult
).现在我有一个简单的 CRUD ViewSet,我可以在其中使用
search_fields
列表来搜索所有需要的字段(在我的例子中是birth_date
、 city
、 phone_number
和full_name
/ is_adult
)。 But here next problems arise:但是这里出现了下一个问题:
search_fields
I can do a search only by all fields specified in the ViewSet's search_fields
(frontend can't search only by city
or other distinct fields if it wants to) - other way I'll need to create a separate ViewSet (and separate URL?) for every combination of fields to filter by.search_fields
我只能通过 ViewSet 的search_fields
中指定的所有字段进行搜索(如果需要,前端不能仅按city
或其他不同的字段进行搜索) - 其他方式我需要创建一个单独的 ViewSet (和单独的 URL ?) 用于过滤的每个字段组合。 Terrific.exact
/ icontains
/etc comparison method on each query.exact
/ icontains
/etc 比较方法。full_name
or is_adult
because they are dynamic model properties, not usual fields.full_name
或is_adult
搜索/过滤,因为它们是动态模型属性,而不是通常的字段。site.com/api/people/?city=^New&full_name=John%20Doe
(perfectly - with opportunity to document query parameters for OpenAPI schema / Swagger)site.com/api/people/?city=^New&full_name=John%20Doe
(完美 - 有机会OpenAPI 模式 / Swagger 的文档查询参数) So maybe someone knows which is the most elegant way to provide a capability of such complex search with Django/DRF?所以也许有人知道用 Django/DRF 提供这种复杂搜索功能的最优雅的方法是什么? In which direction should I look?
我应该朝哪个方向看?
"full_name" alias needs to be part of your queryset. “full_name”别名需要成为您的查询集的一部分。 You can achieve it by queryset annotation.
您可以通过查询集注释来实现它。
In your People view ( api/people/ controller) you have to set your queryset to be:在您的人员视图( api/people/控制器)中,您必须将您的查询集设置为:
from django.db.models.functions import Concat
from django.db.models import Value
queryset = Human.objects.annotate(fullname=Concat('first_name', Value(' '), 'last_name'))
Also, customize your "filter_backed" to act the way you need.此外,自定义您的“filter_backed”以按照您需要的方式行事。
class PeopleFilterBackend(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
params_serializer = PeopleFilterSerializer(data=request.params)
params_serializer.is_valid(raise_exception=True)
valid_params = params_serializer.data
if full_name := valid_params.get("full_name")
queryset = queryset.filter(full_name__icountains=full_name)
# insert here all of other filters logic
return queryset
PeopleFilterSerializer is your custom params serializer, You have to specify there all of your accepted params: PeopleFilterSerializer 是您的自定义参数序列化程序,您必须在那里指定所有接受的参数:
from rest_framework import serializers
class PeopleFilterSerializer(serializers.Serializer):
full_name = serializers.CharField() # set required=False if not required
city = serializer.CharField()
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.