【问题标题】:Django-filter - filter an annotated fieldDjango-filter - 过滤带注释的字段
【发布时间】:2020-03-11 17:06:55
【问题描述】:

我在我的 django-tables2 表上使用 Django-filter,我有一个自定义过滤器,它使用一个搜索框来搜索所有字段。 SiteFilterEx,它获取所有字段名称并创建或查询以搜索每个字段名称。

我的模型有以下内容:

class Site(models.Model):
    location = models.CharField(max_length=50)
    ref = models.CharField(max_length=255, blank=True, null=True)
    bgp_as = models.CharField(max_length=6, verbose_name="BGP AS Number")
    opening_date = models.DateField(verbose_name="Site opening date", blank=True, null=True)
    tel = models.CharField(max_length=20, blank=True, null=True)
    address = models.CharField(max_length=255, blank=True, null=True)
    town = models.CharField(max_length=255, blank=True, null=True)
    postcode = models.CharField(max_length=10, blank=True, null=True)

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)

但是 id 也喜欢搜索带注释的字段“active_circuit”(可以在 QuerySet 中看到) 但是当我将它添加到字段列表中时,我收到错误消息,指出它不存在。 我需要以某种方式将带注释的字段作为有效字段传递给过滤器以进行搜索或其他内容?

过滤器:

class SiteFilterEx(django_filters.FilterSet):
    ex = django_filters.CharFilter(label='Ex filter', method='filter_ex')
    search_fields = ['location', 'sitesupernet__subnet', 'bgp_as', 'opening_date','town','postcode','active_circuit']

    def filter_ex(self, qs, name, value):
        if value:
            q_parts = value.split()

            # Use a global q_totals
            q_totals = Q()

            # This part will get us all possible segmantiation of the query parts and put it in the possibilities list
            combinatorics = itertools.product([True, False], repeat=len(q_parts) - 1)
            possibilities = []
            for combination in combinatorics:
                i = 0
                one_such_combination = [q_parts[i]]
                for slab in combination:
                    i += 1
                    if not slab: # there is a join
                        one_such_combination[-1] += ' ' + q_parts[i]
                    else:
                        one_such_combination += [q_parts[i]]
                possibilities.append(one_such_combination)

            # Now, for all possiblities we'll append all the Q objects using OR
            for p in possibilities:
                list1=self.search_fields
                list2=p
                perms = [zip(x,list2) for x in itertools.permutations(list1,len(list2))]

                for perm in perms:
                    q_part = Q()
                    for p in perm:
                        q_part = q_part & Q(**{p[0]+'__icontains': p[1]})
                    q_totals = q_totals | q_part

            qs = qs.filter(q_totals)
        return qs    

    class Meta:
        model = Site
        fields = ['ex']     
        form = SiteFilterForm  

查看:

class Sites(LoginRequiredMixin, ExportMixin, PagedFilteredTableView):
    model = Site
    table_class = SiteTable
    template_name = "app_settings/table_view.html"
    login_url = '/login/'
    redirect_field_name = 'redirect_to'
    filter_class = SiteFilterEx
    exclude_columns = ("buttons", )
    table_pagination = {'per_page': 12}

    def dispatch(self, *args, **kwargs):
        site_type = get_object_or_404(SiteType, pk=self.kwargs['site_type'])
        site_state = 'Open' if self.kwargs['state'] else 'Closed'
        self.site_type_name = '{} {}s'.format(site_state, site_type.site_type)
        self.site_type_icon = 'fa {}'.format(site_type.icon)
        return super(Sites, self).dispatch(*args, **kwargs)

    def get_queryset(self):
        site_type = self.kwargs['site_type']
        subnet = Subquery(
            DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
                                        active_link=True, \
                                        circuit__decommissioned=False
                                        ).values('circuit__name')[:1])        
        active_circuit = Subquery(
            DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
                                        active_link=True, \
                                        circuit__decommissioned=False
                                        ).values('circuit__name')[:1])

        return super(Sites, self).get_queryset().filter(
                is_live=self.kwargs['state'], site_type_id=site_type
            ).annotate(
                active_circuit=active_circuit
            ).prefetch_related('sitesupernet_set')


    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)     
        context['page_icon']=self.site_type_icon
        context['page_title']=self.site_type_name 
        return context

回溯错误:

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py" in view
  71.             return self.dispatch(request, *args, **kwargs)

File "/itapp/itapp/sites/views.py" in dispatch
  127.         return super(Sites, self).dispatch(*args, **kwargs)

File "/usr/local/lib/python3.6/site-packages/django/contrib/auth/mixins.py" in dispatch
  52.         return super().dispatch(request, *args, **kwargs)

File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py" in dispatch
  97.         return handler(request, *args, **kwargs)

File "/usr/local/lib/python3.6/site-packages/django/views/generic/list.py" in get
  142.         self.object_list = self.get_queryset()

File "/itapp/itapp/sites/views.py" in get_queryset
  142.             return super(Sites, self).get_queryset().filter(

File "/itapp/itapp/itapp/functions.py" in get_queryset
  560.         return self.filter.qs

File "/usr/local/lib/python3.6/site-packages/django_filters/filterset.py" in qs
  237.                 qs = self.filter_queryset(qs)

File "/usr/local/lib/python3.6/site-packages/django_filters/filterset.py" in filter_queryset
  224.             queryset = self.filters[name].filter(queryset, value)

File "/usr/local/lib/python3.6/site-packages/django_filters/filters.py" in __call__
  765.         return self.method(qs, self.f.field_name, value)

File "/itapp/itapp/sites/filters.py" in filter_ex
  53.             qs = qs.filter(q_totals)

File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py" in filter
  892.         return self._filter_or_exclude(False, *args, **kwargs)

File "/usr/local/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
  910.             clone.query.add_q(Q(*args, **kwargs))

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in add_q
  1290.         clause, _ = self._add_q(q_object, self.used_aliases)

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1312.                     current_negated, allow_joins, split_subq, simple_col)

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1318.                     split_subq=split_subq, simple_col=simple_col,

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_filter
  1190.         lookups, parts, reffed_expression = self.solve_lookup_type(arg)

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in solve_lookup_type
  1049.         _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())

File "/usr/local/lib/python3.6/site-packages/django/db/models/sql/query.py" in names_to_path
  1420.                                      "Choices are: %s" % (name, ", ".join(available)))

Exception Type: FieldError at /sites/sites/1/2/
Exception Value: Cannot resolve keyword 'active_circuit' into field. Choices are: acc_short, address,  device, downtimeschedule, id, is_live, last_hw_refresh_date, location, no_wallboard, opening_date, postcode, ref, site_type, site_type_id, sitecircuits, sitecontacts, sitefile, sitenotes, sitesupernet, snmpdata, tel, town, watchlist

【问题讨论】:

  • 首先,我无法理解您的代码要做什么。恕我直言,请从业务角度添加查询。
  • 以及,您所说的 “我目前无法过滤任何带注释的字段” 是什么意思?你试过类似的东西吗?
  • 嗨,我已经编辑并试图重新解释这个问题,这有帮助吗?
  • 可以添加错误回溯吗?另外,是否可以从filter_ex方法中获取指定的注解字段值?
  • 您已经添加了回溯,您从 filter_Ex 获取带注释的字段是什么意思?谢谢

标签: django django-filter django-tables2


【解决方案1】:

尝试提及Subquery的类型,模型可以从django.db import models导入

 active_circuit = Subquery(
                DeviceCircuitSubnets.objects.filter(device__site_id=OuterRef('id'), \
                                            active_link=True, \
                                            circuit__decommissioned=False
                                            ).values('circuit__name')[:1],output_field=models.CharField())

【讨论】:

  • 我认为不会是IntegerField,可能是CharField
  • 是的,它的“电路名称”将 IntegerField 更新为 CharField 。谢谢。
  • 同样的错误,无法将关键字“active_circuit”解析为字段
  • 子查询结果有没有可能存在多对一的关系? , 还有一个突然冒出来的建议,你能定义一个对象模型“circuit__name”并尝试一下吗?让我们看看这个问题是否得到解决,我们将能够深入研究这个问题,并尝试用一种更具推测性的方法来解决它!!!
  • 我已将 DeviceCircuitSubnets 模型添加到问题中以防万一有帮助?
猜你喜欢
  • 1970-01-01
  • 2018-12-30
  • 2013-10-30
  • 1970-01-01
  • 1970-01-01
  • 2011-06-22
  • 2012-04-05
  • 2018-12-23
  • 2021-06-09
相关资源
最近更新 更多