1
votes

I am trying to integrate filters on a Wagtail Page model (which is basically a full Django model, more about that here: http://docs.wagtail.io/en/v2.4/topics/pages.html).

The PlayerDetailPage is like a Post page with detailed information, and the PlayerPage is like a Blog page, with an overview off all the posts.

Here I want to filter on certain fields using Django Filter. However since the Field Position model is an inline model and abstract and connected through a ParentalKey I am not sure how to do this.

If you see the 'agency' field which uses a ForeignKey , I can simply connect this in Django-Filter by using __ but I have no idea how to do this with an Wagtail InlinePanel.

The code:

class FieldPosition(models.Model):
FIELD_POSITION_CHOICES = (
                            ('GK', _('Goalkeeper')),
                            ('LB', _('Left back')),
                            ('LCD', _('Left central defender')),
                            ('CD', _('Central defender')),
                            ('RCD', _('Right central defender')),
                            ('RB', _('Right back')),
                            ('LWB', _('Left wing back')),
                            ...... 

    field_position              = models.CharField(max_length=3, choices=FIELD_POSITION_CHOICES, null=True)
    level                       = models.CharField(max_length=2, choices=FIELD_POSITION_LEVEL, null=True)

    class Meta:
        unique_together = ('field_position', 'level')
        abstract = True

# Relationship between PlayerDetailPage and the FieldPosition model
class FieldPositionRelationship(Orderable, FieldPosition):
    page                        = ParentalKey('PlayerDetailPage', on_delete=models.CASCADE, related_name='field_position')

class PlayerPage(Page):

    # Overriding the Wagtail page model to add extra variables. This is the Wagtail way as per the creator https://stackguides.com/questions/32626815/wagtail-views-extra-context
    def get_context(self, request):
        context = super(PlayerPage, self).get_context(request)

        context['filter'] = PlayerDetailPageFilter(request.GET, queryset=PlayerDetailPage.objects.all())
        return context

class PlayerDetailPage(Page):
    club                        = models.ForeignKey('Clubs', null=True, blank=True, on_delete=models.SET_NULL, related_name='+')

    content_panels              = Page.content_panels + [
                                                        InlinePanel('field_position', label="Field position", max_num=3),
    ]

class PlayerDetailPageFilter(django_filters.FilterSet):
    field_position          = django_filters.filters.ModelMultipleChoiceFilter(field_name='field_position',
        to_field_name='field_position', queryset=FieldPositionRelationship.objects.all())


    class Meta:
        model = PlayerDetailPage
        fields = [ 'club__name', 'field_position' ]

In my template I do this:

<form action="" method="get">
{{ filter.form.as_p }}
<input type="submit" />

{% for page in filter.qs %}

{% for i in page.field_position.all %}
{{ i.field_position }}
{% endfor %}

{% endfor %}

So when I loop over the field_position I can see the variables I declared and those who are attached to the page models. However when I try to use the filter I get this traceback:

Traceback:

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in _resolve_lookup
  835.                     current = current[bit]

During handling of the above exception ('PlayerDetailPageFilter' object is not subscriptable), another exception occurred:

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/core/handlers/exception.py" in inner
  35.             response = get_response(request)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  158.                 response = self.process_exception_by_middleware(e, request)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/core/handlers/base.py" in _get_response
  156.                 response = response.render()

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/response.py" in render
  106.             self.content = self.rendered_content

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/response.py" in rendered_content
  83.         content = template.render(context, self._request)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/backends/django.py" in render
  61.             return self.template.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render
  175.                     return self._render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/test/utils.py" in instrumented_test_render
  98.     return self.nodelist.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render
  943.                 bit = node.render_annotated(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  910.             return self.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  155.             return compiled_parent._render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/test/utils.py" in instrumented_test_render
  98.     return self.nodelist.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render
  943.                 bit = node.render_annotated(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  910.             return self.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/loader_tags.py" in render
  67.                 result = block.nodelist.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render
  943.                 bit = node.render_annotated(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in render_annotated
  910.             return self.render(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/defaulttags.py" in render
  161.                 values = self.sequence.resolve(context, True)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in resolve
  676.                 obj = self.var.resolve(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in resolve
  802.             value = self._resolve_lookup(context)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/template/base.py" in _resolve_lookup
  843.                         current = getattr(current, bit)

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

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

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django_filters/filters.py" in filter
  251.             qs = self.get_method(qs)(q)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/query.py" in filter
  839.         return self._filter_or_exclude(False, *args, **kwargs)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/query.py" in _filter_or_exclude
  857.             clone.query.add_q(Q(*args, **kwargs))

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

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1271.                     current_negated, allow_joins, split_subq)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/sql/query.py" in _add_q
  1277.                     split_subq=split_subq,

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_filter
  1215.         condition = self.build_lookup(lookups, col, value)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/sql/query.py" in build_lookup
  1085.         lookup = lookup_class(lhs, rhs)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/lookups.py" in __init__
  18.         self.rhs = self.get_prep_lookup()

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/fields/related_lookups.py" in get_prep_lookup
  115.                 self.rhs = target_field.get_prep_value(self.rhs)

File "/Users/rafrasenberg/myoden/lib/python3.6/site-packages/django/db/models/fields/__init__.py" in get_prep_value
  947.         return int(value)

Exception Type: ValueError at /pages/players/
Exception Value: invalid literal for int() with base 10: 'RW'
1
Managed to solve this by taking an alternate approach. But ran into a different problem, you can check the new question here: stackoverflow.com/questions/55832348/…Raf Rasenberg
Please don't add answer material to questions. Edits to add addenda are generally OK, but it is not ideal to add them at the start. This is confusing for readers who have not previously seen the question (i.e. most of them). In general, non-answer addenda should go at the end, so they are in the correct chronological order.halfer
Your comment here is fine, but if there is a separate solution to the problem above, would you consider adding a self-answer, if you think that might be helpful to future readers?halfer
For sure! When I solve the other problem I will try to answer this one too.Raf Rasenberg

1 Answers

0
votes

So the problem is in this part of the code:

class PlayerDetailPageFilter(django_filters.FilterSet):
    field_position          = django_filters.filters.ModelMultipleChoiceFilter(field_name='field_position',
        to_field_name='field_position', queryset=FieldPositionRelationship.objects.all())


    class Meta:
        model = PlayerDetailPage
        fields = [ 'club__name', 'field_position' ]

I am referencing to field_position which is the ID of the FieldPositionRelationship so that'll be an integer. I am running my query set on that which is a string, that's why I throws this traceback.

Also the approach of this is wrong because ModelMultipleChoiceFilter should be MultipleChoiceFilter since I only want a hardcoded list of the Field Position choices.

The correct filter should be:

class PlayerDetailPageFilter(FilterSet):
    field_position_relationship__field_position     = filters.ChoiceFilter(choices=FIELD_POSITION_CHOICES)

    class Meta:
        model = PlayerDetailPage
        fields = []