class CanFilterAndExclude(Method): filter_validator: Callable = Options(default=DenyAll) exclude_validator: Callable = Options(default=DenyAll) def set_default_success_code(self, context: Context): raise NotImplementedError() def query_db(self, context: Context): raise NotImplementedError() def schema(self): _schema = super().schema() _schema["filter_schema"] = repr(self.filter_validator) _schema["exclude_schema"] = repr(self.exclude_validator) return _schema
class Example: a = 0 b: int = 0 c: int d: Optional[List] e: Optional[int] = 1 f: int = Options(rename="g")
class Get(NoBodyNoObjectsNoInput): count_total: bool = Options(default=False) def query_db(self, context: Context): self.db_layer.get(context, self) def set_default_success_code(self, context: Context): context.status_code = 200
class RequiredDefaultCallable: _int: int = Options(default=lambda: 1) _float: float = Options(default=lambda: 1.0) _bool: bool = Options(default=lambda: True) _str: str = Options(default=lambda: "x") _dict: dict = Options(default=lambda: {"x": 1}) _list: list = Options(default=lambda: [1, 2])
class RequiredDefault: _int: int = Options(default=1) _float: float = Options(default=1.0) _bool: bool = Options(default=True) _str: str = Options(default="x") _dict: dict = Options(default={"x": 1}) _list: List[int] = Options(default=[1, 2])
class Put(CanFilterAndExclude): def query_db(self, context: Context): self.db_layer.put(context, self) context.items = [] input_validator: Callable = Options(default=DenyAll) def set_default_success_code(self, context: Context): context.status_code = 200 def schema(self): _schema = super().schema() _schema["input_schema"] = repr(self.input_validator) return _schema
class Post(Method): input_validator: Callable = Options(default=DenyAll) def query_db(self, context: Context): self.db_layer.post(context, self) context.items = [] def schema(self): _schema = super().schema() _schema["input_schema"] = repr(self.input_validator) return _schema def set_default_success_code(self, context: Context): context.status_code = 201
class Allowed: a: int = Options(allowed=[1, 2])
class Method: mode: str = 'django' query: Any = None queryset: Any = None db_layer: Optional[DbLayer] = None serializer: Optional[Serializer] = None deserializer: Optional[Deserializer] = None skip_query_db: bool = Options(default=False) input_validator: Callable = Options(default=DenyAll) output_validator: Callable = Options(default=DenyAll) pre_query_hooks: List[Callable] = Options(default=list) post_query_hooks: List[Callable] = Options(default=list) request_hooks: List[Callable] = Options(default=list) response_hooks: List[Callable] = Options(default=list) def __validate_it__post_init__(self): need_fields = [] if self.mode == 'django': need_fields = ['queryset'] if not self.db_layer: self.db_layer = DjangoDbLayer() if not self.serializer: self.serializer = DjangoSerializer() if not self.deserializer: self.deserializer = DjangoDeserializer() for field in need_fields: if getattr(self, field) is None and not self.skip_query_db: raise ValueError( f"Empty `{field}` is allowed only for resources with `skip_query_db` == True" ) def __set_name__(self, owner, name): if not hasattr(owner, 'methods') or not owner.methods: setattr(owner, 'methods', {}) owner.methods[self.__class__.__name__.lower()] = self def schema(self): return {"output_schema": repr(self.output_validator)} def query_db(self, context: Context): raise NotImplementedError() @staticmethod def validate(validator, context: Context): context.items = [validator(item) for item in context.items] def validate_input(self, context: Context): self.validate(self.input_validator, context) def validate_output(self, context: Context): self.validate(self.output_validator, context) @staticmethod def apply_hooks(hooks, context: Context): for hook in hooks: hook(context) def apply_pre_query_hooks(self, context: Context): self.apply_hooks(self.pre_query_hooks, context) def apply_post_query_hooks(self, context: Context): self.apply_hooks(self.post_query_hooks, context) def set_default_success_code(self, context: Context): raise NotImplementedError() def apply_request_hooks(self, context: Context): self.apply_hooks(self.request_hooks, context) def apply_response_hooks(self, context: Context): self.apply_hooks(self.response_hooks, context) def handle(self, request, **kwargs): context = self.deserializer.deserialize(request, self, **kwargs) self.set_default_success_code(context) self.apply_request_hooks(context) self.validate_input(context) self.apply_pre_query_hooks(context) if not self.skip_query_db: self.query_db(context) self.apply_post_query_hooks(context) self.validate_output(context) self.apply_response_hooks(context) return self.serializer.serialize(context)
class Context: request: Any queryset: Any order: dict = Options(default=dict) user_filter: dict = Options(default=dict) user_exclude: dict = Options(default=dict) system_filter: Union[dict, Q, None] = Options(default=None) system_exclude: Union[dict, Q, None] = Options(default=None) merged_filter: Union[dict, Q, None] = Options(default=None) merged_exclude: Union[dict, Q, None] = Options(default=None) project: List[str] = Options(default=list) items: Union[List[dict], QuerySet] = Options(default=list) meta: dict = Options(default=dict) status_code: int = Options(default=0) limit: int = Options(default=0) skip: int = Options(default=0) total: int = Options(default=0) created: int = Options(default=0) updated: int = Options(default=0) deleted: int = Options(default=0)
class Convert: a: str = Options(parser=str)
class J: c: Union[H, I] = Options(auto_pack=True, packer=pack_value)
class A: a: str = Options(default="a") b: str = Options(default="b")
class SerializerType: a: float = Options(parser=float, serializer=int)
class C: c: Union[A, B] = Options(auto_pack=True, packer=pack_value)
class TypeWithValidator: email: str = Options(validators=[is_email])
class NotRequiredDefaultCallable: _optional: Optional[int] = lambda: 0 _optional_with_options: Optional[int] = Options(default=lambda: 0) _required: int = lambda: 0
class NotRequiredDefault: _optional: Optional[int] = 0 _optional_with_options: Optional[int] = Options(default=1) _required: int = 0
class UnexpectedNotStrip: a: float = Options(parser=float, serializer=int)
class OptionalDictWithDefaultC: s: str nested_with_elements: Optional[Dict[int, OptionalDictWithDefaultB]] = Options() nested_empty: Optional[Dict[int, OptionalDictWithDefaultB]] = Options()
class R: a: int = 0 b: str = Options(min_length=3, max_length=10)
class AllowedCallable: a: int = Options(allowed=lambda: [1, 2])
class B: b: int = Options(default=1)
class Amount: a: int = Options(min_value=10) b: int = Options(max_value=20)
class G: c: Union[E, F] = Options(auto_pack=True, packer=pack_value)
class Length: a: str = Options(min_length=2) b: str = Options(max_length=5)
class PlayerB: name: str = Options(default="") items: List[ItemB] = Options(default=list) skills: Dict[str, SkillB] = Options(default=dict)
class OptionalAutoPackEnabled: a: Optional[A] = Options(auto_pack=True, packer=pack_value)
class B: a: int = Options(alias="_a") b: int = Options(alias="_b")
@schema(strip_unknown=True) class First: a: int = 0 b: int = 0 Second = clone(First, strip_unknown=True, include=["a"]) Third = clone(First, strip_unknown=True, exclude=["a"]) # try replace Fourth = clone(First, strip_unknown=True, add=[("a", str, Options(parser=str))]) # add Fifth = clone(First, strip_unknown=True, add=[("_id", int, Options(default=1))]) class CloneTestCase(TestCase): def test_clone(self): data = {"a": 1, "b": 2} first = First(**data) self.assertEquals(data, to_dict(first)) second = Second(**data)