Как использовать пакет django-filter для полей с внешним ключом в Django?
Привет всем!
Новичок в Django, и запутался, помощь приветствуется! Я создал таблицу, благодаря пользователям stackoverflow, типа:
| Organization | Total amount of appeals | Amount of written form appeals | Amount of oral form appeals |
|---|---|---|---|
| Organization 1 | 3 | 1 | 2 |
| Organization 2 | 2 | 1 | 1 |
У вас три модели:
class Organization(models.Model):
organization_name = models.CharField(max_length=50)
class AppealForm(models.Model):
form_name = models.CharField(max_length=50)
class Appeal(models.Model):
organization = models.ForeignKey(Organization, on_delete=models.CASCADE)
appeal_form = models.ForeignKey(AppealForm, on_delete=models.CASCADE)
applicant_name = models.CharField(max_length=150)
appeal_date = models.DateField()
Объекты модели Организация:
| organization_name |
|---|
| Organization 1 |
| Organization 2 |
Объекты модели AppealForm:
| form_name |
|---|
| In written form |
| In oral form |
Объекты модели Appeal:
| organization | appeal_form | applicant_name |
|---|---|---|
| Organization 1 | In written form | First and Last name |
| Organization 1 | In oral form | First and Last name |
| Organization 1 | In oral form | First and Last name |
| Organization 2 | In written form | First and Last name |
| Organization 2 | In oral form | First and Last name |
В функции файла views.py я создал запрос для рендеринга, например:
from django.db.models import Count, Q
organizations = Organization.objects.annotate(
).annotate(
total=Count('appeal'),
total_written=Count('appeal', filter=Q(appeal__appeal_form__form_name='in written form')),
total_oral=Count('appeal', filter=Q('appeal__appeal_form__form_name='in oral form'))
)
Теперь я хочу отфильтровать содержимое таблицы по модели AppealForm и дате обращения (поле appeal_date в Appeal model). Случай: Пользователь открывает таблицу и из строки поиска над таблицей выбирает, какую форму апелляции и/или диапазон дат необходимо просмотреть.
Вопрос: Как отфильтровать запрос, приведенный выше в файле views.py, используя пакет django-filter?
Самым общим способом определения сложного фильтра является использование аргумента method. Я не могу сказать, что полностью понимаю вашу проблему, но таким образом можно применить любой фильтр, для которого вы можете придумать кверисет. В общих чертах:
import django-filters as DF
class SomeFilters( DF.FilterSet):
name = DF.xxxFilter( method='my_method', field_name='object_field', label='whatever', ...)
...
def my_method( self, qs, name, value):
# in here you create a new more restrictive queryset based on qs
# to implement your filter, and return it.
# name is the field name. Note, you don't have to use or follow it
# value is the value that the user typed
qs = qs.filter( ...) # or .exclude, or complicated stuff
return qs
Вот довольно простой метод, который я написал, чтобы создать аннотацию со значением поля, очищенным от пробелов, а затем сделать фильтр text-contains для этого.
def filter_array_desc( self, qs, name, value):
value = value.replace(' ','')
qs = qs.annotate(
arr_d_nospaces = Replace( 'array_desc', Value(' '), Value('')) # delete all spaces
).filter(
arr_d_nospaces__icontains = value )
return qs
Вот общий вариант, который можно применить к любому полю через ChoiceFilter для фильтрации того, пустое поле или нет:
YESNO = (('Y','Yes'), ('N','No'))
marr_b = FD.ChoiceFilter( field_name='marr', label='M_array is blank', method='filter_blank_yesno',
choices=YESNO, empty_label="Don't Care" )
...
def filter_blank_yesno( self, qs, name, value):
if value=="Y":
return qs.filter(**{ name:'' })
elif value=="N":
return qs.exclude( **{ name:'' })
raise ValueError(f'filter_blank_yesno received value="{value}" which is neither "Y" nor "N"')
Надеюсь, это поможет. В основном вы будете фильтровать, следуя взаимосвязям между моделями, используя двойные индексы для перехода между моделями, и, возможно, аннотируя и фильтруя анотации, или делая что-то с Q objucts и тому подобное.