Skip to main content
Glama

Django Styleguide MCP Server

by dmmeteo
list-apis.md•6.32 kB
### List APIs #### Plain A dead-simple list API should look like that: ```python from rest_framework.views import APIView from rest_framework import serializers from rest_framework.response import Response from styleguide_example.users.selectors import user_list from styleguide_example.users.models import BaseUser class UserListApi(APIView): class OutputSerializer(serializers.Serializer): id = serializers.CharField() email = serializers.CharField() def get(self, request): users = user_list() data = self.OutputSerializer(users, many=True).data return Response(data) ``` _Keep in mind this API is public by default. Authentication is up to you._ #### Filters + Pagination At first glance, this is tricky, since our APIs are inheriting the plain `APIView` from DRF, while filtering and pagination are baked into the generic ones: 1. [DRF Filtering](https://www.django-rest-framework.org/api-guide/filtering/) 1. [DRF Pagination](https://www.django-rest-framework.org/api-guide/pagination/) That's why, we take the following approach: 1. Selectors take care of the actual filtering. 1. APIs take care of filter parameter serialization. 1. If you need some of the generic paginations, provided by DRF, the API should take care of that. 1. If you need a different pagination, or you are implementing it yourself, either add a new layer to handle pagination or let the selector do that for you. **Let's look at the example, where we rely on pagination, provided by DRF:** ```python from rest_framework.views import APIView from rest_framework import serializers from styleguide_example.api.mixins import ApiErrorsMixin from styleguide_example.api.pagination import get_paginated_response, LimitOffsetPagination from styleguide_example.users.selectors import user_list from styleguide_example.users.models import BaseUser class UserListApi(ApiErrorsMixin, APIView): class Pagination(LimitOffsetPagination): default_limit = 1 class FilterSerializer(serializers.Serializer): id = serializers.IntegerField(required=False) # Important: If we use BooleanField, it will default to False is_admin = serializers.NullBooleanField(required=False) email = serializers.EmailField(required=False) class OutputSerializer(serializers.Serializer): id = serializers.CharField() email = serializers.CharField() is_admin = serializers.BooleanField() def get(self, request): # Make sure the filters are valid, if passed filters_serializer = self.FilterSerializer(data=request.query_params) filters_serializer.is_valid(raise_exception=True) users = user_list(filters=filters_serializer.validated_data) return get_paginated_response( pagination_class=self.Pagination, serializer_class=self.OutputSerializer, queryset=users, request=request, view=self ) ``` When we look at the API, we can identify few things: 1. There's a `FilterSerializer`, which will take care of the query parameters. If we don't do this here, we'll have to do it elsewhere & DRF serializers are great at this job. 1. We pass the filters to the `user_list` selector 1. We use the `get_paginated_response` utility, to return a .. paginated response. Now, let's look at the selector: ```python import django_filters from styleguide_example.users.models import BaseUser class BaseUserFilter(django_filters.FilterSet): class Meta: model = BaseUser fields = ('id', 'email', 'is_admin') def user_list(*, filters=None): filters = filters or {} qs = BaseUser.objects.all() return BaseUserFilter(filters, qs).qs ``` As you can see, we are leveraging the powerful [`django-filter`](https://django-filter.readthedocs.io/en/stable/) library. > šŸ‘€ The key thing here is that the selector is responsible for the filtering. You can always use something else, as a filtering abstraction. For most of the cases, `django-filter` is more than enough. Finally, let's look at `get_paginated_response`: ```python from rest_framework.response import Response def get_paginated_response(*, pagination_class, serializer_class, queryset, request, view): paginator = pagination_class() page = paginator.paginate_queryset(queryset, request, view=view) if page is not None: serializer = serializer_class(page, many=True) return paginator.get_paginated_response(serializer.data) serializer = serializer_class(queryset, many=True) return Response(data=serializer.data) ``` This is basically a code, extracted from within DRF. Same goes for the `LimitOffsetPagination`: ```python from collections import OrderedDict from rest_framework.pagination import LimitOffsetPagination as _LimitOffsetPagination from rest_framework.response import Response class LimitOffsetPagination(_LimitOffsetPagination): default_limit = 10 max_limit = 50 def get_paginated_data(self, data): return OrderedDict([ ('limit', self.limit), ('offset', self.offset), ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data) ]) def get_paginated_response(self, data): """ We redefine this method in order to return `limit` and `offset`. This is used by the frontend to construct the pagination itself. """ return Response(OrderedDict([ ('limit', self.limit), ('offset', self.offset), ('count', self.count), ('next', self.get_next_link()), ('previous', self.get_previous_link()), ('results', data) ])) ``` What we basically did is reverse-engineered the generic APIs. > šŸ‘€ Again, if you need something else for pagination, you can always implement it & use it in the same manner. There are cases, where the selector needs to take care of the pagination. We approach those cases the same way we approach filtering. You can find the code for the example list API with filters & pagination in the [Styleguide Example](https://github.com/HackSoftware/Styleguide-Example#example-list-api) project.

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/dmmeteo/django-styleguide-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server