DRF - django_filters -Использование пользовательских методов

У меня есть такая модель:

class Agreement(models.Model):
    file_no = models.IntegerField(primary_key=True)
    contract_date = models.DateField()
    contract_time = models.IntegerField()

    @property
    def calculate_expiry_date(self):
        return self.contract_date + relativedelta(years=self.contract_time)

    @property
    def is_expired(self):
        return (self.contract_date + relativedelta(years=self.contract_time)) < timezone.now().date() 

Функция is_expired возвращает true или false для каждого соглашения

и у меня есть простой фильтр вроде этого:

class AgreementFilter(filters.FilterSet):
    file_no = filters.NumberFilter(lookup_expr='icontains')

    class Meta:
        model = Agreement
        fields = ['file_no',] 

Я думаю, что не могу фильтровать поле свойства, потому что фильтры Django работают на уровне базы данных. Итак, как я могу заставить фильтровать объекты модели соглашения, если они действительны или недействительны, истинны или ложны?


person Poised Enigma    schedule 18.05.2020    source источник
comment
Что вы подразумеваете под «заставить фильтровать соглашение»?   -  person isAif    schedule 18.05.2020
comment
Я хочу отфильтровать объекты модели соглашения, если они действительны или недействительны.   -  person Poised Enigma    schedule 18.05.2020


Ответы (2)


Вы можете добавить новое поле в модель Agreement, в которой сохраняется дата истечения срока действия соглашения:

expiry_date = models.DateField()

Если поле contract_time указывает количество лет, в течение которых контракт остается в силе, и вы знаете его во время добавления нового контракта, тогда вы можете рассчитать дату истечения срока действия так же, как вы делаете сейчас, и добавить ее в поле expiry_date без использования декоратора свойств. .

Теперь вы хотите, чтобы это делалось автоматически при добавлении новых записей, для этого вы можете использовать pre_save сигналы.

from django.dispatch import receiver
from django.db.models import signals

@receiver(signals.pre_save, sender=WorkDone)
def calculate_expiry_date(sender, instance, **kwargs):
    instance.expiry_date = instances.contract_date + \
       relativedelta(years=instance.contract_time)

Для части фильтрации вы можете использовать текущую дату для фильтрации записей. Поскольку вы используете DRF, это будет довольно просто, во внешнем интерфейсе просто используйте текущую дату и передайте ее в качестве аргумента фильтра, чтобы показать только те соглашения, дата которых больше текущей даты.

person isAif    schedule 19.05.2020
comment
Спасибо за решение ... но я сделал это, написав собственный фильтр, который для меня является более простым методом. - person Poised Enigma; 25.05.2020

Мне удалось написать собственный фильтр для фильтрации просроченных и действительных соглашений, используя параметр «метод» для указания пользовательского метода — см. документацию по django-filter

from datetime import timedelta
from django.db.models import F, Case, When, BooleanField
from django_filters import rest_framework as filters

class AgreementFilter(filters.FilterSet):

    file_no = filters.NumberFilter(lookup_expr='icontains')
    is_expired = filters.BooleanFilter(method='filter_is_expired')

    class Meta:
        model = Agreement
        fields = ['file_no',] 

      def filter_is_expired(self, queryset, name, value):
        if value is not None:
            queryset = queryset.annotate(

                expired=Case(When(contract_date__lt=timezone.now().date() - (timedelta(days=365) * F('contract_time')),
                             then=True), default=False, output_field=BooleanField())).filter(expired=value)
        return queryset

Queryset вернет значение, если объект соглашения является истинным или ложным путем вычисления:

Для соглашений с истекшим сроком действия

http://127.0.0.1:8000/api/agreement/?is_expired=True

и Для действующих соглашений

http://127.0.0.1:8000/api/agreement/?is_expired=False
person Poised Enigma    schedule 25.05.2020