简体   繁体   中英

How to query related object in DRF viewsets.ModelViewSet

I have a serialised model (Job) which I am using with datatables. The "Job" model is related to another model (Board) and here is where my problem is. I followed the doc here to filter jobs that are related to the "Board" model which is currently being viewed, but I can't get it to work as intended.

models.py

class Board(models.Model):
    name = models.CharField(_('board name'), max_length=256)
    slug = models.SlugField(_('unique url'), null=True, blank=True)
...


class Job(models.Model):
    board = models.ForeignKey(Board, on_delete=models.CASCADE, verbose_name=_('board'))
...

views.py

 class JobDataTablesViewSet(viewsets.ModelViewSet):
        queryset = Job.objects.all().order_by('-date_posted')
        serializer_class = JobDatatablesSerializer
        filter_backends = (DatatablesFilterBackend,)
        filterset_class = JobGlobalFilter
    
        def get_queryset(self):
            slug = self.kwargs['slug']
            queryset = Job.objects.filter(board__slug=slug)
            return queryset

urls.py

path('<slug:slug>/', views.BoardPublicView.as_view(), name='public-board')

You have to give the __ for ForeignKey lookups. So, get_queryset method be like:

slug = models.SlugField(
    unique=True,
    default=self.slug, # This is not from the tutorial, it's a modification
    # to demonstrate here.
    max_length=13,
)

def get_queryset(self):
    slug = self.kwargs['slug']
    queryset = Job.objects.filter(board__slug=slug)
    return queryset

For anyone with a similar issue, kindly take a look at alanjds/drf-nested-routers . This is what I used and it worked perfectly.

Here is an overview of what I did.

After installing the package with pip install drf-nested-routers .

According to the docs,

It is not needed to add this library in your Django project's settings.py file, as it does not contain any app, signal or model.

In my app urls.py file, I created parent and child routers

router = routers.SimpleRouter()
router.register(r'boards', views.BoardViewSet, basename="boards_api_list")

boards_router = routers.NestedSimpleRouter(router, r'boards', lookup='board')
boards_router.register(r'jobs', views.JobViewSet, basename='board_jobs_list')

urlpatterns = [
    path('api/', include(router.urls)),
    path('api/', include(boards_router.urls)),
]

Then in my serializers.py , I created a HyperlinkedModelSerializer

class BoardSerializer(serializers.HyperlinkedModelSerializer):
    owner = UserSerializer()
    jobs = NestedHyperlinkedRelatedField(
        many=True,
        read_only=True,
        view_name='jobs_view_name',
        parent_lookup_kwargs={'slug': 'board__slug'}
    )

    class Meta:
        model = Board
        fields = [...]
        extra_kwargs = {
            'url': {'view_name': 'my_custom_view_name', 'lookup_field': 'slug'},
            ...other desired kwargs...
        }

class JobViewSet(serializers.ModelSerializer):
    ...standard serializer settings...

I went on to my views.py to create my viewsets and added the get_queryset params

class JobViewSet(viewsets.ModelViewSet):
    queryset = Job.objects.all().order_by('-date_posted')
    serializer_class = JobSerializer
    filter_backends = (DatatablesFilterBackend,)
    filterset_class = JobGlobalFilter

    def get_queryset(self):
        return Job.objects.filter(board__slug=self.kwargs['board_slug'])

Once you do these, you should be able to access your APIs via

/parent_url/{{parent_pk}}/child_list/{{child_pk}}

Now, because of my peculiar need (I wanted to use this on datatables to filter related objects), I added the URLs of my APIs to the head of my base.html so that the URLs can be loaded dynamically since I can't load such in my javascript file

...
<head>
    <script>var boardListUrl= "{% url 'boards:boards_api_list-list' %}?format=datatables"</script>
    {% if board.slug %}
    <script>var jobListUrl= "{% url 'boards:board_jobs_list-list' board.slug %}?format=datatables"</script>
    {% endif %}
<head>
...

And in my main.py file, I did the following

 var table = $('#tableName').DataTable({
        ...
        "ajax": jobListUrl,
        ...
});

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM