I’m using DRF for a simple API, and I was wondering if there’s a way to achieve this behavior:
- I’ve got two models similar to the following:
class Table(models.Model): name = models.CharField(max_length=100) ... class Column(models.Model): original_name = models.CharField(max_length=100) name = models.CharField(max_length=100, blank=True, null=True) ... table = models.ForeignKey(Table, on_delete=models.CASCADE, related_name="columns")
- And their serializers as follows:
class ColumnSerializer(serializers.HyperlinkedModelSerializer): table = serializers.HyperlinkedRelatedField( read_only=True, view_name="table-detail" ) class Meta: model = Column fields = ["url", "name", "table"] class TableSerializer(serializers.HyperlinkedModelSerializer): dataset = serializers.HyperlinkedRelatedField( read_only=True, view_name="dataset-detail" ) tags = serializers.SlugRelatedField( many=True, slug_field="name", queryset=Tag.objects.all() ) columns = ColumnSerializer(many=True, read_only=True) class Meta: model = Table fields = [ "url", "name", ... "columns", ]
- This returns me an output similar to
{ ... "results": [ { "url": "http://0.0.0.0:8001/api/tables/1/", "name": "some-name", "columns": [ { "url": "http://0.0.0.0:8001/api/columns/1/", "name": "id", "table": "http://0.0.0.0:8001/api/tables/1/" }, ... }
which is totally fine. But what I’d really want to do is, if a Column
has name=None
, it’s filtered out from every API ViewSet. I’ve managed to do it on the ColumnViewSet
by doing queryset = queryset.filter(name__isnull=False)
, but I can’t do it for the TableViewSet
or others that might show a Column
list.
I’ve tried tinkering with the ColumnSerializer
, but the best I could get from it was to show null
s on the Column
list.
I wonder if there’s a way of hiding those.
EDIT 1: Adding my ViewSets
class TableViewSet(viewsets.ModelViewSet): serializer_class = TableSerializer def get_queryset(self): queryset = Table.objects.all().order_by("name") # some query_params filtering return queryset class ColumnViewSet(viewsets.ModelViewSet): serializer_class = ColumnSerializer def get_queryset(self): queryset = Column.objects.all().order_by("id") queryset = queryset.filter(name__isnull=False) # some query_params filtering return queryset
Advertisement
Answer
You can work with a Prefetch
object [Django-doc] to filter the related object collection, so:
from django.db.models import Prefetch class TableViewSet(viewsets.ModelViewSet): serializer_class = TableSerializer def get_queryset(self): queryset = Table.objects.prefetch_related( Prefetch('columns', Column.objects.filter(name__isnull=False)) ).order_by('name') # some query_params filtering return queryset