Фильтр по полям из отношений foreignKey
У меня есть куча моделей, и некоторые из них связаны (отношениями foreign-key), и я написал сериализатор, который позволяет мне вывести все связанные поля, которые я хочу, и оставить то, что я не хочу видеть. Отлично. Теперь у меня есть базовый фильтр, который использует модель (PmP
), содержащую все foreignkey, но теперь я хочу добавить еще один фильтр для поля (имя поля e
из PmPr
модели) из другой модели, той, которая считывается через соединение с иностранным ключом (li
в модели PmP
соединяется с моделью PmL
, содержащей поле pro
, которая соединяется с моделью PmPr
, где находится поле e
). Но я не знаю, как это сделать, и, насколько я понимаю, я не могу установить два filter_class
внутри моего view
(PmPLListView
)?! И я не знаю, как получить доступ к полю через отношение foreignkey. Так как же мне поступить? Если я могу получить доступ к полю e
из модели PmPr
через мой существующий фильтр - это тоже меня устроит, мне не нужно два класса фильтра (если это вообще возможно). Это была просто моя первая мысль. (btw. извините за странные имена, но, к сожалению, мне не разрешено писать настоящие имена)
вот мои модели (по крайней мере, актуальные):
class PmP(models.Model):
created_at = models.DateTimeField()
pr = models.DecimalField(max_digits=6, decimal_places=2)
li = models.ForeignKey(PmL, models.DO_NOTHING)
se = models.ForeignKey('PmSe', models.DO_NOTHING)
class Meta:
managed = False
db_table = 'pm_p'
class PmL(models.Model):
u = models.TextField()
pro = models.ForeignKey('PmPr', models.DO_NOTHING)
sh = models.ForeignKey('PmS', models.DO_NOTHING)
active = models.IntegerField()
class Meta:
managed = False
db_table = 'pm_l'
class PmSe(models.Model):
name = models.TextField()
s_i_id = models.TextField(blank=True, null=True)
sh = models.ForeignKey('PmS',
models.DO_NOTHING,
blank=True,
null=True)
class Meta:
managed = False
db_table = 'pm_se'
class PmPr(models.Model):
name = models.TextField()
e = models.CharField(max_length=13)
created_at = models.DateTimeField()
cus = models.ForeignKey(PmC, models.DO_NOTHING)
u_v_p = models.DecimalField(max_digits=10,
decimal_places=2,
blank=True,
null=True)
cf = models.IntegerField(blank=True, null=True)
s_k_u = models.IntegerField(blank=True, null=True)
class Meta:
managed = False
db_table = 'pm_pr'
вот как выглядит мой сериализатор:
class PmPLSerializer(serializers.ModelSerializer):
# id = serializers.SerializerMethodField('get_l_id')
u = serializers.SerializerMethodField('get_l_u')
sh = serializers.SerializerMethodField('get_sh_name')
name = serializers.SerializerMethodField('get_pro_name')
e = serializers.SerializerMethodField('get_pro_e')
u_v_p = serializers.SerializerMethodField('get_pro_u_v_p')
s_k_u = serializers.SerializerMethodField('get_pro_s_k_u')
se = serializers.SerializerMethodField('get_se_name')
pr = serializers.SerializerMethodField('get_pr')
created_at = serializers.SerializerMethodField('get_created_at')
class Meta:
model = PmP
# fields = '__all__'
fields = ('u', 'sh', 'name', 'e', 's_k_u', 'u_v_p', 'pr',
'created_at', 'se')
depth = 2
def get_l_id(self, obj):
return obj.li.id
def get_l_u(self, obj):
return obj.li.u
def get_sh_name(self, obj):
return obj.li.sh.name
def get_pro_name(self, obj):
return obj.li.pro.name
def get_pro_e(self, obj):
return obj.li.pro.e
def get_pro_u_v_p(self, obj):
return obj.li.pro.u_v_p
def get_pro_s_k_u(self, obj):
return obj.li.pro.s_k_u
def get_se_name(self, obj):
return obj.se.name
def get_pr(self, obj):
return obj.pr
def get_created_at(self, obj):
return obj.created_at
это мой класс фильтра:
class PmPFilter(rfilters.FilterSet):
class Meta:
model = PmP
fields = [
"created_at",
"pr",
]
for field in ["pr"]:
exec(f'min_{field} = rfilters.NumberFilter(field, lookup_expr="gte")')
exec(f'max_{field} = rfilters.NumberFilter(field, lookup_expr="lte")')
# filter by date as "is_less_than_or_equal_to"
written_to = rfilters.CharFilter(method="created_at_to", label="created_at to")
# filter by date as "is_greater_than_or_equal_to"
written_from = rfilters.CharFilter(method="created_at_from", label="created_at from")
# filter by exact date
written = rfilters.CharFilter(method="created_at_exact", label="created_at exact")
def created_at_exact(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at=cdate)
def created_at_to(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at__lte=cdate)
def created_at_from(self, queryset, name, value):
year, month, day, hour, minute, second = self.parse_date(value)
cdate = datetime(year, month, day, hour, minute, second)
return queryset.filter(created_at__gte=cdate)
def parse_date(self, value):
return (
parser.parse(value).year,
parser.parse(value).month,
parser.parse(value).day,
parser.parse(value).hour,
parser.parse(value).minute,
parser.parse(value).second,
)
И, наконец, вот мое мнение:
class PmPLListView(generics.ListAPIView):
queryset = PmP.objects.all()
serializer_class = PmPLSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ["created_at", "pr"]
filter_class = PmPFilter
fields = ("created_at", "pr")
filter_fields = fields
search_fields = fields
def get_queryset(self):
"""
This view should return a list of all data
"""
return PmP.objects.filter()
О, я понял! Я могу получить доступ к внешнему отношению с двумя символами подчеркивания. Поэтому я изменил свой класс Filter следующим образом:
class PmPFilter(rfilters.FilterSet):
class Meta:
model = PmPrice
fields = [
"created_at",
"pr",
"li__pro__e",
]
...
и внутри моего представления PmPLListView
я также добавил двойное подчеркивание для доступа к полю:
class PmPLListView(generics.ListAPIView):
queryset = PmP.objects.all()
serializer_class = PmPLSerializer
filter_backends = (DjangoFilterBackend, SearchFilter, OrderingFilter)
ordering_fields = ["created_at", "pr"]
filter_class = PmPFilter
fields = ("created_at", "pr", "li__pro__e")
filter_fields = fields
search_fields = fields
теперь я могу фильтровать по полю e