NSJ Rest Lib
nsj-rest-lib is a Python library for building declarative REST APIs, adhering to internal guidelines and focusing on DTO (Data Transfer Object) and Entity patterns. It simplifies API creation by abstracting common patterns for data mapping, validation, and persistence. The current version is 6.4.1, and it maintains an active release cadence with regular feature additions and bug fixes.
Common errors
-
KeyError: 'some_field_name' or ValueError: 'some_field_name' is not a valid field for this DTO/Entity
cause Attempting to update an entity field that is not explicitly defined in the DTO used for the update, or using an outdated DTO definition, especially after upgrading to v4.0.0.fixVerify that all fields intended for update are explicitly defined in the DTO used for the update operation. Ensure DTOs are kept up-to-date with entity definitions, reflecting the v4.0.0 breaking change. -
AttributeError: 'NoneType' object has no attribute 'id' (or other field) in POST response handling.
cause The default POST response might not return the full created object, especially before v3.0.0 or if `retrieve_after_insert` is not explicitly enabled, leading to client-side errors when trying to access properties.fixEnsure `retrieve_after_insert=True` is set on your route or service configuration for POST requests if your client expects the full object (with its ID and other properties) to be returned after an insert (available in v3.0.0+). -
nsj_rest_lib.exceptions.FieldNotWritableException: Field 'my_read_only_field' is read-only.
cause You are attempting to write data to a DTOField that has been explicitly marked with `read_only=True`.fixEither remove the `read_only=True` flag from the `DTOField` definition if the field should be modifiable, or ensure that your API requests for `POST`, `PUT`, or `PATCH` do not include data for this specific field. -
TypeError: can't instantiate abstract class EntityBase with abstract methods __annotations__ (or similar for DTOBase)
cause You are trying to directly instantiate an abstract base class like `EntityBase` or `DTOBase` instead of defining a concrete subclass that inherits from it.fixAlways create a new class that inherits from `EntityBase` or `DTOBase` and define its fields. For example, `class MyConcreteEntity(EntityBase): ...` or `class MyConcreteDTO(DTOBase): ...`.
Warnings
- breaking Update queries behavior changed in v4.0.0. Previously, all fields in an `Entity` were considered for an `UPDATE` SQL statement, potentially setting non-DTO fields to `NULL`. Now, only fields explicitly defined in the `DTO` being used for the update are included.
- gotcha The `DTOAggregator` (introduced in v4.9.0) allows nesting DTOs in the JSON representation, but the underlying data for both the parent and aggregated DTO fields are expected to come from a single, flat entity/table in the database.
- gotcha After a `POST` operation, the default behavior (before v3.0.0 and if not explicitly configured) might not return the complete created object in the response. This can lead to front-end issues expecting a full object echo.
- gotcha Using `read_only=True` on a `DTOField` (introduced in v2.8.0) will prevent that field from being writable during `POST`, `PUT`, or `PATCH` operations, even if it's included in the request payload.
Install
-
pip install nsj-rest-lib
Imports
- DTOBase
from nsj_rest_lib.dto.dto_base import DTOBase
- DTOField
from nsj_rest_lib.dto.fields import DTOField
- DTOAggregator
from nsj_rest_lib.dto.fields import DTOAggregator
- DTOSQLJoinField
from nsj_rest_lib.dto.fields import DTOSQLJoinField
- EntityBase
from nsj_rest_lib.entity.entity_base import EntityBase
- ServiceBase
from nsj_rest_lib.service.service_base import ServiceBase
Quickstart
from dataclasses import dataclass
from nsj_rest_lib.dto.dto_base import DTOBase
from nsj_rest_lib.dto.fields import DTOField, DTOAggregator
from nsj_rest_lib.entity.entity_base import EntityBase
# 1. Define your Entity (maps to a database table)
@dataclass
class ProductEntity(EntityBase):
id: int
name: str
description: str
price: float
# 2. Define your DTO (Data Transfer Object, maps to API representation)
class ProductDTO(DTOBase):
id: int = DTOField(pk=True)
name: str = DTOField()
price: float = DTOField(decimal_places=2)
# 3. Example of an aggregated DTO using DTOAggregator (v4.9.0+)
class ProductDetailsDTO(DTOBase):
category: str = DTOField()
weight_kg: float = DTOField()
class FullProductDTO(DTOBase):
id: int = DTOField(pk=True)
name: str = DTOField()
description: str = DTOField()
details: ProductDetailsDTO = DTOAggregator()
print("DTOs and Entities defined successfully.")
print(f"ProductDTO fields: {ProductDTO.get_fields()}")
print(f"FullProductDTO fields: {FullProductDTO.get_fields()}")