【问题标题】:Django: Search results with django-tables2 and django-filterDjango:使用 django-tables2 和 django-filter 搜索结果
【发布时间】:2021-04-13 13:48:56
【问题描述】:

我想通过搜索表单检索模型的对象,但为搜索分数添加另一列。我不确定如何使用 django-tables2 和 django-filter 来实现这一点。

将来,我希望用户能够使用 django-filter 来帮助过滤搜索结果。我可以从PeopleSearchListView 访问表单变量,但也许这是集成 django 表单以进行表单处理的更好方法?

到目前为止,我的想法是处理 get_queryset() 中的 get 请求,然后在将查询集发送到 PeopleTable 之前对其进行修改,但是向查询集中添加另一列似乎不是标准方法。

tables.py

class PeopleTable(tables.Table):
   score = tables.Column()
   class Meta:
     model = People
     template_name = 'app/bootstrap4.html'
     exclude = ('id',)
     sequence = ('score', '...')

views.py

class PeopleFilter(django_filters.FilterSet):
  class Meta:
    model = People
    exclude = ('id',)

class PeopleSearchListView(SingleTableMixin, FilterView):
  table_class = PeopleTable
  model = People
  template_name = 'app/people.html'
  filterset_class = PeopleFilter
  
  def get_queryset(self):
    p = self.request.GET.get('check_this')
    qs = People.objects.all()
    ####
    # Run code to score users against "check_this".
    # The scoring code I'm using is complex, so below is a simpler
    # example.
    # Modify queryset using output of scoring code?
    ####
    for person in qs:
      if person.first_name == 'Phil' and q == 'Hey!':
        score = 1
      else:
        score = 0
    return qs

urls.py

 urlpatterns = [
   ...
   path('search/', PeopleSearchListView.as_view(), name='search_test'),
   ... ]

models.py

 class People(models.model):
   first_name = models.CharField(max_length=200)
   last_name = models.CharField(max_length=200)

编辑: 评分算法比上面的例子要复杂一些。在最终将每个得分行与搜索查询进行比较之前,它需要对 People 表中的所有行进行完整遍历以生成得分矩阵。这不是一次性的分数。例如:

 def get_queryset(self):
   all = []
   for person in qs:
     all.append(person.name)
   # Do something complex with all,
   # e.g., measure cosine distance between every person,
   # and finally compare to the get request
   scores = measure_cosine(all, self.request.GET.get('check_this'))
   # We now have the scores for each person.
   
 
 
 
 

【问题讨论】:

  • 如何计算分数?
  • @markwalker_ 评分算法有点简单。但我添加了一个评分 sn-p 的示例来提供帮助。重要的是模型中的每一行都有一个分数,我想在可过滤表中输出该行旁边的所有分数。

标签: python django django-queryset django-filter django-tables2


【解决方案1】:

因此您可以在初始化表时添加额外的列。

我有几个表根据系统中的事件执行此操作;

    def __init__(self, *args, **kwargs):
        """
        Override the init method in order to add dynamic columns as
        we need to declare one column per existent event on the system.
        """
        extra_columns = []

        events = Event.objects.filter(
            enabled=True,
        ).values(
            'pk', 'title', 'city'
        )

        for event in events:
            extra_columns.append((
                event['city'],
                MyColumn(event_pk=event['pk'])
            ))

        if extra_columns:
            kwargs.update({
                'extra_columns': extra_columns
            })

        super().__init__(*args, **kwargs)

因此,您可以在提供分数后添加与此类似的分数列。也许将您的分数从视图传递到表中,以便您可以识别它们是否存在并添加列,然后在呈现列时使用数据。

extra_columns 似乎不在 tables2 文档中,但您可以在此处找到代码; https://github.com/jieter/django-tables2/blob/master/django_tables2/tables.py#L251

【讨论】:

  • 有趣!但是我怎样才能将参数传递给表格呢?由于我使用的是 django-filter,因此将数据传递给 PersonTable 类有点不透明。到目前为止,我已经尝试在.get_queryset() 中覆盖self.table_data 并在PersonTable 的__init__ 中读取它,这有点成功。此时我无法触发render_score 函数。只有当我使用 .Column(accessor="xyz") 时,render_score 列才会被触发。
  • 您可以提供一个名为get_table_kwargs 的方法。也许您创建了一个将对象 ID 映射为其分值的键的字典?
【解决方案2】:

当你为 django-tables2 定义一个不包含在表数据或查询集中的新列时,你应该提供一个渲染方法来计算它的值。

如果需要复杂的过滤、预处理或连接,您不必覆盖 get_queryset。

在你的表类中:

class PeopleTable(tables.Table):    
    score = tables.Column(accessor="first_name")
    class Meta:
        model = People

    def render_score(self, record):
        return 1 if record["first_name"] == "Phil" and q == "Hey!" else 0

在您看来,您可以使用 get_context_data 覆盖和提供复杂数据以及特殊过滤或聚合:

def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["filter"] = self.filter

        aggs = {
            "score": Function("..."),
            "other": Sum("..."),
        }
        _data = (
            People.objects.filter(**params)
            .values(*values)
            .annotate(**aggs)
            .order_by(*values)
            .distinct()
        )

        df = pandas.DataFrame(_data)
        df = df....
        chart_data = df.to_json()
        data = df.to_dict()...
      
        self.table = PeopleTable(data)
        context["table"] = self.table      
        context['chart_data']=chart_data

        return context

【讨论】:

  • 看起来不错,我不知道访问器可以这样使用。不幸的是,评分算法必须在评分之前遍历所有记录一次。这是一个复杂的评分算法。
猜你喜欢
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多