Django Rest API-使用搜索过滤器搜索方法字段

Ale*_*exW 9 django django-rest-framework django-rest-viewsets

我试图过滤搜索剩余的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') 
Run Code Online (Sandbox Code Playgroud)

意见:

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'
    )
Run Code Online (Sandbox Code Playgroud)

如何在搜索字段中包含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()
                )
            )
        )
Run Code Online (Sandbox Code Playgroud)

序列化器

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')  
Run Code Online (Sandbox Code Playgroud)

模型:

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)
Run Code Online (Sandbox Code Playgroud)

错误:

Exception Type: ImproperlyConfigured at /api/subnets/
Exception Value: Field name `safe_subnet` is not valid for model `DeviceCircuitSubnets`.
Run Code Online (Sandbox Code Playgroud)

bdo*_*leu 5

您需要使用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()
                )
            )
        )
Run Code Online (Sandbox Code Playgroud)

然后,在序列化程序中,您可以使用以下内容。

def get_safe_subnet(self, obj):
    return obj.safe_subnet
Run Code Online (Sandbox Code Playgroud)


MrN*_*ame 0

与其他(优秀)答案完全不同的方向。既然您希望能够频繁过滤该safe_subnet字段,为什么不让它成为模型中的实际数据库字段呢?您可以在其中一种save方法期间计算并填充/更新该值,然后就可以django-filters了。这还有一个优点是允许直接通过 SQL 完成过滤,理论上可以提供更好的性能。