Ассоциировать классы с django-фильтрами - PullRequest
0 голосов
/ 02 июля 2018

Добрый день, у меня вопрос по поводу django-фильтров. Моя проблема: В моем файле models.py определены два класса:

class Volcano(models.Model):
    vd_id = models.AutoField("ID, Volcano Identifier (Index)",
                         primary_key=True)
    [...]

class VolcanoInformation(models.Model):

    # Primary key
    vd_inf_id = models.AutoField("ID, volcano information identifier (index)",
                             primary_key=True)

    # Other attributes
    vd_inf_numcal = models.IntegerField("Number of calderas")
    [...]

    # Foreign key(s)
    vd_id = models.ForeignKey(Volcano, null=True, related_name='vd_inf_vd_id',
                          on_delete=models.CASCADE)

Два из них связаны через атрибут vd_id.

Я хочу разработать инструмент поиска, который позволит пользователю искать вулкан по количеству кальдер (vd_inf_numcal).

Я использую django-фильтры, а пока вот мои filters.py:

from .models import *
import django_filters

class VolcanoFilter(django_filters.FilterSet):

    vd_name = django_filters.ModelChoiceFilter(
                                      queryset=Volcano.objects.values_list('vd_name', flat=True),
                                 widget=forms.Select, label='Volcano name',
                                 to_field_name='vd_name',
                                 )

    vd_inf_numcal = django_filters.ModelChoiceFilter(
                                   queryset=VolcanoInformation.objects.values_list('vd_inf_numcal', flat=True),
                                 widget=forms.Select, label='Number of calderas',
                                 )


    class Meta:
        model = Volcano
        fields = ['vd_name', 'vd_inf_numcal']

Мои views.py:

def search(request):
    feature_list = Volcano.objects.all()
    feature_filter = VolcanoFilter(request.GET, queryset = feature_list)

    return render(request, 'app/search_list.html', {'filter' : feature_filter, 'feature_type': feature_type})

В моем приложении появляется раскрывающийся список возможного количества кальдер, но поиск не дает результата, который является нормальным, поскольку нет никакой связи между VolcanoInformation.vd_inf_numcal, VolcanoInformation.vd_id и Volcano.vd_id.

В нем даже говорится «Выберите правильный выбор. Этот выбор не является одним из доступных вариантов».

Мой вопрос: как я могу создать эту ссылку, используя django_filters? Думаю, мне следует написать какой-нибудь метод в классе, но я абсолютно не знаю, как это сделать.

Если бы у кого-нибудь был ответ, я был бы более чем благодарен!

Ответы [ 2 ]

0 голосов
/ 17 июля 2018

Спасибо большое! Это именно то, что я искал! Я не понял, как работает .filter().

То, что я сделал для других атрибутов, это для генерации выбора, но другим способом. Например, если бы я просто хотел отобразить список доступных мест, я бы использовал:

# Location attribute
loc = VolcanoInformation.objects.values_list('vd_inf_loc', flat=True)
vd_inf_loc = django_filters.ChoiceFilter(
                              field_name='vd_inf_vd_id__vd_inf_loc',
                              label='Geographic location',
                              choices=zip(loc, loc),
                                        )
0 голосов
/ 14 июля 2018

В общем, вам нужно ответить на два вопроса:

  • Какое поле мы запрашиваем и какие выражения запроса / поиска необходимо сгенерировать.
  • Какие виды значений мы должны фильтровать.

Эти ответы по сути являются левой и правой стороной вашего .filter() вызова.

В этом случае вы фильтруете обратную сторону отношения Информация о вулкане (vd_inf_vd_id) по количеству кальдер (vd_inf_numcal) для вулкана. Кроме того, вы хотите exact совпадение.

Для значений вам понадобится набор вариантов, содержащих целые числа.

AllValuesFilter рассмотрит столбец БД и сгенерирует выбор из значений столбца. Однако недостатком является то, что выбор не будет включать пропущенные значения, которые выглядят странно при визуализации. Вы можете либо адаптировать это поле, либо использовать простой ChoiceFilter, генерируя значения самостоятельно.

def num_calderas_choices():
    # Get the maximum number of calderas
    max_count = VolcanoInformation.objects.aggregate(result=Max('vd_inf_numcal'))['result']

    # Generate a list of two-tuples for the select dropdown, from 0 to max_count
    # e.g, [(0, 0), (1, 1), (2, 2), ...]
    return zip(range(max_count), range(max_count))

class VolcanoFilter(django_filters.FilterSet):
    name = ...
    num_calderas = django_filters.ChoiceFilter(
        # related field traversal (note the connecting '__')
        field_name='vd_inf_vd_id__vd_inf_numcal',
        label='Number of calderas',
        choices=num_calderas_choices
    )

class Meta:
    model = Volcano
    fields = ['name', 'num_calderas']

Обратите внимание, что я сам не тестировал приведенный выше код, но он должен быть достаточно близко, чтобы начать работу.

...