Skip to content

ViewSets

Base classes and mixins that add consistent response wrapping, exception handling, and routing conventions to DRF ViewSets.

Mixins

PublicIDLookupMixin

Configures a ViewSet to look up objects by public_id instead of the internal primary key, keeping internal IDs out of your URLs.

Attribute Value
lookup_field public_id
lookup_url_kwarg id

Usage

class UserViewSet(PublicIDLookupMixin, ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

A detail request to /users/abc123/ resolves internally as:

User.objects.get(public_id="abc123")

Tip

Place PublicIDLookupMixin before the ViewSet in the MRO so its lookup_field takes precedence.

CommonMixin

Shared convenience helpers available on every ViewSet that inherits from it.

request_user

A property that returns request.user — avoids repeatedly accessing self.request.user inside action methods.

def perform_create(self, serializer):
    serializer.save(owner=self.request_user)

_response(data=None, status_code=200, **kwargs)

A thin wrapper around rest_framework.response.Response with a default 200 status.

Parameter Type Default Description
data any None Response payload
status_code int HTTP_200_OK HTTP status code
**kwargs Forwarded to Response
def my_action(self, request):
    return self._response({"acknowledged": True}, status_code=202)

SuccessResponseMixin

Intercepts finalize_response to wrap every successful response in the drf-corekit envelope, so action methods never need to wrap their own payloads.

Success envelope

{
  "data": { "id": 1, "name": "Alice" },
  "errors": null
}

Bypass condition

Responses with a status of 207 or ≥ 300 that already carry a structured error body are passed through unchanged:

{
  "type": "validation_error",
  "errors": [{ "field": "email", "message": "Enter a valid email." }]
}

Note

You do not need to call wrap_success_response yourself — any Response returned from your ViewSet is wrapped automatically.

ViewSets

All ViewSets below compose ExceptionMapperMixin, SuccessResponseMixin, and CommonMixin on top of the corresponding DRF base class.

ExceptionMapperMixin

Translates arbitrary application exceptions into DRF-compatible APIException instances before they reach the DRF exception handler, and logs a full traceback at ERROR level.

def my_action(self, request):
    # ApplicationError is caught, logged, and re-raised as an APIException
    return self.handle_base_exception(self._do_work, request.data)

ModelViewSet

A full CRUD ViewSet with response wrapping and exception mapping pre-applied.

Actions

Method Action
GET /resource/ list
POST /resource/ create
GET /resource/<id>/ retrieve
PUT /resource/<id>/ update
PATCH /resource/<id>/ partial_update
DELETE /resource/<id>/ destroy
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

ReadOnlyModelViewSet

Exposes read-only endpoints only — no writes permitted.

Actions

Method Action
GET /resource/ list
GET /resource/<id>/ retrieve
class CountryViewSet(ReadOnlyModelViewSet):
    queryset = Country.objects.all()
    serializer_class = CountrySerializer

ReadUpdateModelViewSet

Allows reading and updating records, but not creating or deleting them.

Actions

Method Action
GET /resource/ list
GET /resource/<id>/ retrieve
PUT /resource/<id>/ update
PATCH /resource/<id>/ partial_update
class ProfileViewSet(ReadUpdateModelViewSet):
    queryset = Profile.objects.all()
    serializer_class = ProfileSerializer

APIViewSet

A plain APIView with the drf-corekit conventions (response wrapping, exception mapping, request_user) applied. Use this for non-model, action-oriented endpoints that don't need a router.

class HealthCheckViewSet(APIViewSet):
    def get(self, request):
        return self._response({"status": "ok"})