Django rest api - 使用搜索过滤器搜索方法字段
Django rest api - searching a method field with the search filter
我正在尝试过滤搜索其余 api 页面,并希望将方法字段用作搜索字段之一,但是当我这样做时,我收到一条错误消息,指出该字段无效,然后它将我模型中的字段列为唯一有效来源
序列化器:
class SubnetDetailsSerializer(QueryFieldsMixin, serializers.HyperlinkedModelSerializer):
subnet = serializers.SerializerMethodField()
device = serializers.ReadOnlyField(
source='device.hostname',
)
circuit_name = serializers.ReadOnlyField(
source='circuit.name',
)
subnet_name = serializers.ReadOnlyField(
source='subnet.description',
)
safe_subnet = serializers.SerializerMethodField()
def get_safe_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask.replace('/','_'))
def get_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask)
class Meta:
model = DeviceCircuitSubnets
fields = ('id','device_id','subnet_id','circuit_id','subnet','safe_subnet','subnet_name','device','circuit_name')
观看次数:
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all().select_related('circuit','subnet','device')
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
如何在搜索字段中包含 safe_subnet?
谢谢
编辑
这是现在的代码
views.py
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
def get_queryset(self):
return (
super().get_queryset()
.select_related('circuit','subnet','device')
.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
)
)
serializer.py
class SubnetDetailsSerializer(QueryFieldsMixin, serializers.HyperlinkedModelSerializer):
subnet = serializers.SerializerMethodField()
device = serializers.ReadOnlyField(
source='device.hostname',
)
circuit_name = serializers.ReadOnlyField(
source='circuit.name',
)
subnet_name = serializers.ReadOnlyField(
source='subnet.description',
)
def get_safe_subnet(self, obj):
return getattr(obj, 'safe_subnet', None)
def get_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask)
class Meta:
model = DeviceCircuitSubnets
fields = ('id','device_id','subnet_id','circuit_id','subnet','safe_subnet','subnet_name','device','circuit_name')
型号:
class DeviceCircuitSubnets(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
circuit = models.ForeignKey(Circuit, on_delete=models.CASCADE, blank=True, null=True)
subnet = models.ForeignKey(Subnet, on_delete=models.CASCADE)
active_link = models.BooleanField(default=False, verbose_name="Active Link?")
active_link_timestamp = models.DateTimeField(auto_now=True, blank=True, null=True)
错误:
Exception Type: ImproperlyConfigured at /api/subnets/
Exception Value: Field name `safe_subnet` is not valid for model `DeviceCircuitSubnets`.
您需要使用 safe_subnet
属性注释您的查询集,以便它可以搜索。
from django.db.models import F, Value as V
from django.db.models.functions import Concat, Replace
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
def get_queryset(self):
return (
super().get_queryset()
.select_related('circuit','subnet','device')
.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
)
)
然后在您的序列化程序中,您可以使用以下内容。
def get_safe_subnet(self, obj):
return obj.safe_subnet
上一个 annotate
的回答是一个很好的开始:
from .rest_filters import DeviceCircuitSubnetsFilter
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
# That's where hint lays
filter_class = DeviceCircuitSubnetsFilter
#filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
#No need to override your queryset
现在 rest_filters.py
from django_filters import rest_framework as filters
from django.db.models import F, Value as V
from django.db.models.functions import Concat, Replace
#.... import models
class DeviceCircuitSubnets(filters.FilterSet):
safe_subnet = filters.CharFilter(
name='safe_subnet',
method='safe_subnet_filter')
def safe_subnet_filter(self, queryset, name, value):
"""
Those line will make ?safe_subnet=your_pk available
"""
return queryset.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
).filter(safe_subnet=value)
)
class Meta:
model = DeviceCircuitSubnets
# See https://django-filter.readthedocs.io/en/master/guide/usage.html#generating-filters-with-meta-fields
# This pattern is definitely a killer!
fields = {
'device': ['exact', 'in'],
'circuit': ['exact', 'in'],
'subnet': ['exact', 'in'],
'active_link': ['exact'],
'active_link_timestamp': ['lte', 'gte']
}
请注意:我在文件管理器中注释 safe_subnet
,具体取决于您使用它的程度,您可能需要在您的模型管理器中进行设置!
与其他(优秀)答案完全不同的方向。既然您希望能够频繁地在 safe_subnet
字段上进行过滤,为什么不让它成为您模型中的实际数据库字段呢?您可以在您的 save
方法之一期间计算并 populate/update 值,然后让 django-filters
执行它。这还有一个优点是可以直接通过 SQL 进行过滤,理论上可以提供更好的性能。
我正在尝试过滤搜索其余 api 页面,并希望将方法字段用作搜索字段之一,但是当我这样做时,我收到一条错误消息,指出该字段无效,然后它将我模型中的字段列为唯一有效来源
序列化器:
class SubnetDetailsSerializer(QueryFieldsMixin, serializers.HyperlinkedModelSerializer):
subnet = serializers.SerializerMethodField()
device = serializers.ReadOnlyField(
source='device.hostname',
)
circuit_name = serializers.ReadOnlyField(
source='circuit.name',
)
subnet_name = serializers.ReadOnlyField(
source='subnet.description',
)
safe_subnet = serializers.SerializerMethodField()
def get_safe_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask.replace('/','_'))
def get_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask)
class Meta:
model = DeviceCircuitSubnets
fields = ('id','device_id','subnet_id','circuit_id','subnet','safe_subnet','subnet_name','device','circuit_name')
观看次数:
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all().select_related('circuit','subnet','device')
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
如何在搜索字段中包含 safe_subnet?
谢谢
编辑 这是现在的代码
views.py
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
def get_queryset(self):
return (
super().get_queryset()
.select_related('circuit','subnet','device')
.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
)
)
serializer.py
class SubnetDetailsSerializer(QueryFieldsMixin, serializers.HyperlinkedModelSerializer):
subnet = serializers.SerializerMethodField()
device = serializers.ReadOnlyField(
source='device.hostname',
)
circuit_name = serializers.ReadOnlyField(
source='circuit.name',
)
subnet_name = serializers.ReadOnlyField(
source='subnet.description',
)
def get_safe_subnet(self, obj):
return getattr(obj, 'safe_subnet', None)
def get_subnet(self, obj):
return '{}{}'.format(obj.subnet.subnet, obj.subnet.mask)
class Meta:
model = DeviceCircuitSubnets
fields = ('id','device_id','subnet_id','circuit_id','subnet','safe_subnet','subnet_name','device','circuit_name')
型号:
class DeviceCircuitSubnets(models.Model):
device = models.ForeignKey(Device, on_delete=models.CASCADE)
circuit = models.ForeignKey(Circuit, on_delete=models.CASCADE, blank=True, null=True)
subnet = models.ForeignKey(Subnet, on_delete=models.CASCADE)
active_link = models.BooleanField(default=False, verbose_name="Active Link?")
active_link_timestamp = models.DateTimeField(auto_now=True, blank=True, null=True)
错误:
Exception Type: ImproperlyConfigured at /api/subnets/
Exception Value: Field name `safe_subnet` is not valid for model `DeviceCircuitSubnets`.
您需要使用 safe_subnet
属性注释您的查询集,以便它可以搜索。
from django.db.models import F, Value as V
from django.db.models.functions import Concat, Replace
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
filter_class = DeviceCircuitSubnets
filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
def get_queryset(self):
return (
super().get_queryset()
.select_related('circuit','subnet','device')
.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
)
)
然后在您的序列化程序中,您可以使用以下内容。
def get_safe_subnet(self, obj):
return obj.safe_subnet
上一个 annotate
的回答是一个很好的开始:
from .rest_filters import DeviceCircuitSubnetsFilter
class SubnetDetailsSet(viewsets.ReadOnlyModelViewSet):
queryset = DeviceCircuitSubnets.objects.all()
serializer_class = SubnetDetailsSerializer
permission_classes = (IsAdminUser,)
# That's where hint lays
filter_class = DeviceCircuitSubnetsFilter
#filter_backends = (filters.SearchFilter,)
search_fields = (
'device__hostname',
'circuit__name',
'subnet__subnet',
'safe_subnet'
)
#No need to override your queryset
现在 rest_filters.py
from django_filters import rest_framework as filters
from django.db.models import F, Value as V
from django.db.models.functions import Concat, Replace
#.... import models
class DeviceCircuitSubnets(filters.FilterSet):
safe_subnet = filters.CharFilter(
name='safe_subnet',
method='safe_subnet_filter')
def safe_subnet_filter(self, queryset, name, value):
"""
Those line will make ?safe_subnet=your_pk available
"""
return queryset.annotate(
safe_subnet=Concat(
F('subnet__subnet'),
Replace(F('subnet__mask'), V('/'), V('_')),
output_field=CharField()
)
).filter(safe_subnet=value)
)
class Meta:
model = DeviceCircuitSubnets
# See https://django-filter.readthedocs.io/en/master/guide/usage.html#generating-filters-with-meta-fields
# This pattern is definitely a killer!
fields = {
'device': ['exact', 'in'],
'circuit': ['exact', 'in'],
'subnet': ['exact', 'in'],
'active_link': ['exact'],
'active_link_timestamp': ['lte', 'gte']
}
请注意:我在文件管理器中注释 safe_subnet
,具体取决于您使用它的程度,您可能需要在您的模型管理器中进行设置!
与其他(优秀)答案完全不同的方向。既然您希望能够频繁地在 safe_subnet
字段上进行过滤,为什么不让它成为您模型中的实际数据库字段呢?您可以在您的 save
方法之一期间计算并 populate/update 值,然后让 django-filters
执行它。这还有一个优点是可以直接通过 SQL 进行过滤,理论上可以提供更好的性能。