Module xmodel_rest.api

Expand source code
from xinject import XContext
from .client import RestClient
from .errors import XynRestError
from .settings import RestSettings
from xmodel.remote import RemoteApi, RemoteModel
from typing import TypeVar, List, Tuple, Iterable, Union, Type, get_type_hints, TYPE_CHECKING
from logging import getLogger
from xurls.url import URLStr
from xmodel import Field
from abc import ABC
from .structure import RestStructure
from .auth import RestAuth
from xsentinels import Default
from xurls.url import HTTPGet, HTTPPatch, HTTPDelete, URL
from .auth import RestAuth
from .auth import RestAuth
from typing import TypeVar
from .model import RestModel


# It's really bound to `RestModel`, but I need an easier way to tie into lazy-loading
# BaseModel.__init_subclass__ system, so I can lazy-load my own stuff first before it does.
# Decided it was an exercise for future day. We can live with it for now.
M = TypeVar("M", bound=RemoteModel)


class RestApi(RemoteApi[M]):
    """ Base `xynlib.orm.base.api.BaseApi` subclass generally used by Rest API's.

        Things specific and common to rest api's should go in this class.

        See parent `xynlib.orm.base.api.BaseApi` for things in common among all API's.
    """

    # Telling system about the default/base rest types we want to use with `RestApi`.
    client: RestClient[M]
    structure: RestStructure[Field]
    auth: RestAuth
    settings: RestSettings

    # todo: decide if we should just remove the below, not strictly needed, more of a convenience.
    #
    # Only used for IDE so it knows what type should be here, not used to know which Model to
    # allocate object.
    # This is because RestModel will tell/pass this into RestApi via `__init__`,
    # it happens when a RestModel/BaseModel is created (in BaseModel.__init__).
    model: RestModel[M]

    def send(self, url: URLStr = None):
        """ REQUIRES associated model object [see self.model].

        Convenience method to send this single object to API, it simply calls
        `xynlib.orm.base.client.Client.send_objs` with a single object in the list
        (via `xynlib.orm.base.api.BaseApi.model`).

        If you want to send multiple objects, call `xynlib.orm.base.client.Client.send_objs`.

        Example is below, it uses a made-up rest model called 'SomeRestModelSubclass'.

        (I did not provide all details it would need to use the made-up/imagained rest-api;
        trying to illisrate a basic point here is all.
        If you want more details on how to make a real full/valid rest-model subclass
        see #INSERT-README-LINK#.)

        >>> from xmodel_rest import RestModel
        >>> class SomeRestModelSubclass(RestModel, base_url="....etc...."):
        ...     pass  # Some attributes from the rest-api go here
        >>> obj1 = SomeRestModelSubclass()
        >>> obj2 = SomeRestModelSubclass()
        >>> RestModel.api.client.send_objs([obj1, obj2])

        If you pass in a `url` paramater to the `send_objs` method, the url gets appended to the
        final constructed url before the url gets validated.

        If the url is validated, it will use that final url [with passed in `url` this appended].
        For more information about how URL's are appended to each-other see:
        `xurls.url.URLMutable.append_url`.

        The response from API will update all the values on this object with the results
        of the change [all fields will be updated] and with the latest values from API.

        You can check for errors on model object via `xmodel.remote.api.response_state`, ie:

        >>> from xynlib.orm import BaseModel
        >>> obj: BaseModel
        >>> # Check response_state to see if it had an error:
        >>> obj.api.response_state.had_error
        False
        """
        # Redirect to client.send_objs:
        self.client.send_objs([self.model], url=url)

    # This is a resource-type, see `def auth()` doc-comment below for more details.
    # Subclasses can override this type-hint, and `RestApi` will allocate the new
    # type instead automatically, on demand.
    #
    # The type-hints inform this class what type of objects to create when `auth` along
    # with other special attributes such as `client` and `structure` are needed/asked-for.
    #
    # You can override the type by making your own type-hint on a sub-class.
    # See xmodel.base.api.BaseApi and xmodel.remote.api.RemoteApi for its various special
    # type-hinted attributes for more details, it has more detailed comments/documentation on it.
    auth: RestAuth

    @property
    def _auth(self):
        """
        Treated a `xyn_resaource.context.Resource`, a context resource for the purposes of sharing
        auth credentials. The type-hint assoicated with `auth: XYZ` will be used to grab
        a resource of that type from the current context each time we are asked.

        Thus resource is the auth object used by your `xmodel.base.client.BaseClient` subclass,
        (such as `xynlib.orm.rest.RestClient`),
        to set what type should be used for this, in your BaseClient sub-class, make a type-hint
        like below.

        Let's say you have an auth class you want to use:

        >>> import xmodel.base.auth
        >>> import xmodel
        >>> class MyCoolAuthClass(xmodel.base.auth.RelationAuth)
        ...     pass

        You can set a type-hint for it like so, and it will be automatiaclly used when needed:

        >>> class MyApi(xmodel.RemoteApi):
        ...     auth: MyCoolAuthClass

        Doing that is enough, `xynlib.orm.rest.RestClient` class will see the type-hint and will
        grab one of that type from the XContext and return it.
        In the example above, it would be a `MyCoolAuthClass` type.

        The type-hint is lazily cached in self for fast lookup in the future.

        To see details on what the Auth object should do,
        see `xmodel.base.auth.BaseAuth`.
        """

        auth_type: Type[RestAuth] = self._auth_type
        if not auth_type:
            # Will get all type-hints, and ensure they are valid type refrences
            # (otherwise will error out)
            auth_type = get_type_hints(type(self)).get('auth', RestAuth)
            self._auth_type = auth_type

        # Auth has tokens we want to try and share, treat it as a resource.
        return auth_type.grab()

    _settings_type: Type[RestSettings] = None

    @property
    def _settings(self):
        """ The config object that this api uses, can be customized per-model. All you have to
            do is this to make it a different type:

            >>> import xmodel
            >>>
            >>> class MySettings(BaseSettings):
            ...     my_custom_var: str = xmodel.ConfigVar(
            ...         "MY_CUSTOM_ENVIRONMENTAL_VAR",
            ...         "default"
            ...     )
            >>> class MyApi(xmodel.BaseApi[M]):
            ...     settings: MySettings
            >>> class MyModel(xmodel.model.BaseModel['MyModel']):
            ...     api: MyApi

            The type-hints are enough to tell the system what types to use. They also will
            tell any IDE in use about what type it should be, for type-completion.
            So it's sort of doing double-duty!
        """
        config_type = self._settings_type
        if not config_type:
            config_type = get_type_hints(type(self)).get('settings', None)
            self._settings_type = config_type  # Cache config-type.
            if config_type is None:
                raise XynRestError(
                    f"BaseClient subclass type is undefined for model class ({self.model_type}), "
                    f"a type-hint for 'client' on BaseApi class must be in place for me to know "
                    f"what type to get."
                )

        return XContext.current(for_type=config_type)

    # PyCharm has some sort of issue, if I provide property type-hint and then a property function
    # that implements it. For some reason, this makes it ignore the type-hint in subclasses
    # but NOT in the current class.  It's some sort of bug. This gets around it since pycharm
    # can't figure out what's going on here.
    auth = _auth
    settings = _settings

Classes

class RestApi (*, api: BaseApi[M] = None, model: BaseModel = None)

Base xynlib.orm.base.api.BaseApi subclass generally used by Rest API's.

Things specific and common to rest api's should go in this class.

See parent xynlib.orm.base.api.BaseApi for things in common among all API's.

Warning: You can probably skip the rest (below)

Most of the time you don't create BaseApi objects your self, and so for most people you can skip the following unless you want to know more about internal details.

Init Method Specifics

Normally you would not create an BaseApi object directly your self. BaseModel's know how to do this automatically. It happens in BaseModel.__init_subclass__().

Details about how the arguments you can pass are below.

BaseModel Class Construction:

If you provide an api arg without a model arg; we will copy the BaseApi.structure into new object, resetting the error status, and internal BaseApi._state to None. This api object is supposed to be the parent BaseModel's class api object.

If both api arg + model arg are None, the BaseModel is the root/generic BaseModel (ie: it has no parent BaseModel).

This is what is done by BaseModel classes while the class is lazily loading and creating/configuring the BaseModel class and it's associated BaseApi object (accessible via BaseModel.api)

BaseModel Instance Creation:

If you also pass in a model arg; this get you a special copy of the api you passed in for use just with that BaseModel instance. The model BaseApi._state will be allocated internally in the init'd BaseApi object. This is how a BaseModel instance get's it's own associated BaseApi object (that's a different instance vs the one set on BaseModel class when the BaseModel class was originally constructed).

All params are optional.

Args

api

The "parent" BaseApi obj to copy the basic structure from as a starting point, etc. The superclasses BaseApi class is passed via this arg. This is only used when allocating a new BaseApi object for a new BaseModel class (not an instance, a model class/type). This BaseApi object is used for the class-level BaseModel api object; ie: via "ModelClass.api"

See above "BaseModel Class Construction" for more details.

model

BaseModel to associate new BaseApi obj with. This is only used to create a new BaseApi object for a BaseModel instance for an already-existing type. ie: for BaseModel object instances.

See above "BaseModel Instance Creation" for more details.

Expand source code
class RestApi(RemoteApi[M]):
    """ Base `xynlib.orm.base.api.BaseApi` subclass generally used by Rest API's.

        Things specific and common to rest api's should go in this class.

        See parent `xynlib.orm.base.api.BaseApi` for things in common among all API's.
    """

    # Telling system about the default/base rest types we want to use with `RestApi`.
    client: RestClient[M]
    structure: RestStructure[Field]
    auth: RestAuth
    settings: RestSettings

    # todo: decide if we should just remove the below, not strictly needed, more of a convenience.
    #
    # Only used for IDE so it knows what type should be here, not used to know which Model to
    # allocate object.
    # This is because RestModel will tell/pass this into RestApi via `__init__`,
    # it happens when a RestModel/BaseModel is created (in BaseModel.__init__).
    model: RestModel[M]

    def send(self, url: URLStr = None):
        """ REQUIRES associated model object [see self.model].

        Convenience method to send this single object to API, it simply calls
        `xynlib.orm.base.client.Client.send_objs` with a single object in the list
        (via `xynlib.orm.base.api.BaseApi.model`).

        If you want to send multiple objects, call `xynlib.orm.base.client.Client.send_objs`.

        Example is below, it uses a made-up rest model called 'SomeRestModelSubclass'.

        (I did not provide all details it would need to use the made-up/imagained rest-api;
        trying to illisrate a basic point here is all.
        If you want more details on how to make a real full/valid rest-model subclass
        see #INSERT-README-LINK#.)

        >>> from xmodel_rest import RestModel
        >>> class SomeRestModelSubclass(RestModel, base_url="....etc...."):
        ...     pass  # Some attributes from the rest-api go here
        >>> obj1 = SomeRestModelSubclass()
        >>> obj2 = SomeRestModelSubclass()
        >>> RestModel.api.client.send_objs([obj1, obj2])

        If you pass in a `url` paramater to the `send_objs` method, the url gets appended to the
        final constructed url before the url gets validated.

        If the url is validated, it will use that final url [with passed in `url` this appended].
        For more information about how URL's are appended to each-other see:
        `xurls.url.URLMutable.append_url`.

        The response from API will update all the values on this object with the results
        of the change [all fields will be updated] and with the latest values from API.

        You can check for errors on model object via `xmodel.remote.api.response_state`, ie:

        >>> from xynlib.orm import BaseModel
        >>> obj: BaseModel
        >>> # Check response_state to see if it had an error:
        >>> obj.api.response_state.had_error
        False
        """
        # Redirect to client.send_objs:
        self.client.send_objs([self.model], url=url)

    # This is a resource-type, see `def auth()` doc-comment below for more details.
    # Subclasses can override this type-hint, and `RestApi` will allocate the new
    # type instead automatically, on demand.
    #
    # The type-hints inform this class what type of objects to create when `auth` along
    # with other special attributes such as `client` and `structure` are needed/asked-for.
    #
    # You can override the type by making your own type-hint on a sub-class.
    # See xmodel.base.api.BaseApi and xmodel.remote.api.RemoteApi for its various special
    # type-hinted attributes for more details, it has more detailed comments/documentation on it.
    auth: RestAuth

    @property
    def _auth(self):
        """
        Treated a `xyn_resaource.context.Resource`, a context resource for the purposes of sharing
        auth credentials. The type-hint assoicated with `auth: XYZ` will be used to grab
        a resource of that type from the current context each time we are asked.

        Thus resource is the auth object used by your `xmodel.base.client.BaseClient` subclass,
        (such as `xynlib.orm.rest.RestClient`),
        to set what type should be used for this, in your BaseClient sub-class, make a type-hint
        like below.

        Let's say you have an auth class you want to use:

        >>> import xmodel.base.auth
        >>> import xmodel
        >>> class MyCoolAuthClass(xmodel.base.auth.RelationAuth)
        ...     pass

        You can set a type-hint for it like so, and it will be automatiaclly used when needed:

        >>> class MyApi(xmodel.RemoteApi):
        ...     auth: MyCoolAuthClass

        Doing that is enough, `xynlib.orm.rest.RestClient` class will see the type-hint and will
        grab one of that type from the XContext and return it.
        In the example above, it would be a `MyCoolAuthClass` type.

        The type-hint is lazily cached in self for fast lookup in the future.

        To see details on what the Auth object should do,
        see `xmodel.base.auth.BaseAuth`.
        """

        auth_type: Type[RestAuth] = self._auth_type
        if not auth_type:
            # Will get all type-hints, and ensure they are valid type refrences
            # (otherwise will error out)
            auth_type = get_type_hints(type(self)).get('auth', RestAuth)
            self._auth_type = auth_type

        # Auth has tokens we want to try and share, treat it as a resource.
        return auth_type.grab()

    _settings_type: Type[RestSettings] = None

    @property
    def _settings(self):
        """ The config object that this api uses, can be customized per-model. All you have to
            do is this to make it a different type:

            >>> import xmodel
            >>>
            >>> class MySettings(BaseSettings):
            ...     my_custom_var: str = xmodel.ConfigVar(
            ...         "MY_CUSTOM_ENVIRONMENTAL_VAR",
            ...         "default"
            ...     )
            >>> class MyApi(xmodel.BaseApi[M]):
            ...     settings: MySettings
            >>> class MyModel(xmodel.model.BaseModel['MyModel']):
            ...     api: MyApi

            The type-hints are enough to tell the system what types to use. They also will
            tell any IDE in use about what type it should be, for type-completion.
            So it's sort of doing double-duty!
        """
        config_type = self._settings_type
        if not config_type:
            config_type = get_type_hints(type(self)).get('settings', None)
            self._settings_type = config_type  # Cache config-type.
            if config_type is None:
                raise XynRestError(
                    f"BaseClient subclass type is undefined for model class ({self.model_type}), "
                    f"a type-hint for 'client' on BaseApi class must be in place for me to know "
                    f"what type to get."
                )

        return XContext.current(for_type=config_type)

    # PyCharm has some sort of issue, if I provide property type-hint and then a property function
    # that implements it. For some reason, this makes it ignore the type-hint in subclasses
    # but NOT in the current class.  It's some sort of bug. This gets around it since pycharm
    # can't figure out what's going on here.
    auth = _auth
    settings = _settings

Ancestors

Class variables

var default_converters : Dict[Type[Any], Converter]

Inherited from: RemoteApi.default_converters

For an overview of type-converts, see Type Converters Overview

Instance variables

var authRestAuth

Treated a xyn_resaource.context.Resource, a context resource for the purposes of sharing auth credentials. The type-hint assoicated with auth: XYZ will be used to grab a resource of that type from the current context each time we are asked.

Thus resource is the auth object used by your xmodel.base.client.BaseClient subclass, (such as xynlib.orm.rest.RestClient), to set what type should be used for this, in your BaseClient sub-class, make a type-hint like below.

Let's say you have an auth class you want to use:

>>> import xmodel.base.auth
>>> import xmodel
>>> class MyCoolAuthClass(xmodel.base.auth.RelationAuth)
...     pass

You can set a type-hint for it like so, and it will be automatiaclly used when needed:

>>> class MyApi(xmodel.RemoteApi):
...     auth: MyCoolAuthClass

Doing that is enough, xynlib.orm.rest.RestClient class will see the type-hint and will grab one of that type from the XContext and return it. In the example above, it would be a MyCoolAuthClass type.

The type-hint is lazily cached in self for fast lookup in the future.

To see details on what the Auth object should do, see xmodel.base.auth.BaseAuth.

Expand source code
@property
def _auth(self):
    """
    Treated a `xyn_resaource.context.Resource`, a context resource for the purposes of sharing
    auth credentials. The type-hint assoicated with `auth: XYZ` will be used to grab
    a resource of that type from the current context each time we are asked.

    Thus resource is the auth object used by your `xmodel.base.client.BaseClient` subclass,
    (such as `xynlib.orm.rest.RestClient`),
    to set what type should be used for this, in your BaseClient sub-class, make a type-hint
    like below.

    Let's say you have an auth class you want to use:

    >>> import xmodel.base.auth
    >>> import xmodel
    >>> class MyCoolAuthClass(xmodel.base.auth.RelationAuth)
    ...     pass

    You can set a type-hint for it like so, and it will be automatiaclly used when needed:

    >>> class MyApi(xmodel.RemoteApi):
    ...     auth: MyCoolAuthClass

    Doing that is enough, `xynlib.orm.rest.RestClient` class will see the type-hint and will
    grab one of that type from the XContext and return it.
    In the example above, it would be a `MyCoolAuthClass` type.

    The type-hint is lazily cached in self for fast lookup in the future.

    To see details on what the Auth object should do,
    see `xmodel.base.auth.BaseAuth`.
    """

    auth_type: Type[RestAuth] = self._auth_type
    if not auth_type:
        # Will get all type-hints, and ensure they are valid type refrences
        # (otherwise will error out)
        auth_type = get_type_hints(type(self)).get('auth', RestAuth)
        self._auth_type = auth_type

    # Auth has tokens we want to try and share, treat it as a resource.
    return auth_type.grab()
var clientRestClient[~M]

Inherited from: RemoteApi.client

Returns an appropriate concrete RemoteClient subclass. We figure out the proper client object to use based on the type-hint for …

var contextXContext

Inherited from: RemoteApi.context

BaseApi context to use when asking this object to send/delete/etc its self to/from service …

var have_changes : bool

Inherited from: RemoteApi.have_changes

Is True if self.json(only_include_changes=True) is not None; see json() method for more details.

var modelBaseModel[~M]

Inherited from: RemoteApi.model

REQUIRES associated model object [see doc text below] …

var model_type : Type[~M]

Inherited from: RemoteApi.model_type

The same BaseApi class is meant to be re-used for any number of Models, and so a BaseModel specifies it's BaseApi type as generic BaseApi[M]. In …

var optionsApiOptions[~M]

Inherited from: RemoteApi.options

A set of options you can modify for the current context. If a particular option inside the options object is not set, Options object may look at the …

var response_stateResponseState[~M]

Inherited from: RemoteApi.response_state

Returns the HTTP/Communication state of the api object …

var settingsRestSettings

The config object that this api uses, can be customized per-model. All you have to do is this to make it a different type:

>>> import xmodel
>>>
>>> class MySettings(BaseSettings):
...     my_custom_var: str = xmodel.ConfigVar(
...         "MY_CUSTOM_ENVIRONMENTAL_VAR",
...         "default"
...     )
>>> class MyApi(xmodel.BaseApi[M]):
...     settings: MySettings
>>> class MyModel(xmodel.model.BaseModel['MyModel']):
...     api: MyApi

The type-hints are enough to tell the system what types to use. They also will tell any IDE in use about what type it should be, for type-completion. So it's sort of doing double-duty!

Expand source code
@property
def _settings(self):
    """ The config object that this api uses, can be customized per-model. All you have to
        do is this to make it a different type:

        >>> import xmodel
        >>>
        >>> class MySettings(BaseSettings):
        ...     my_custom_var: str = xmodel.ConfigVar(
        ...         "MY_CUSTOM_ENVIRONMENTAL_VAR",
        ...         "default"
        ...     )
        >>> class MyApi(xmodel.BaseApi[M]):
        ...     settings: MySettings
        >>> class MyModel(xmodel.model.BaseModel['MyModel']):
        ...     api: MyApi

        The type-hints are enough to tell the system what types to use. They also will
        tell any IDE in use about what type it should be, for type-completion.
        So it's sort of doing double-duty!
    """
    config_type = self._settings_type
    if not config_type:
        config_type = get_type_hints(type(self)).get('settings', None)
        self._settings_type = config_type  # Cache config-type.
        if config_type is None:
            raise XynRestError(
                f"BaseClient subclass type is undefined for model class ({self.model_type}), "
                f"a type-hint for 'client' on BaseApi class must be in place for me to know "
                f"what type to get."
            )

    return XContext.current(for_type=config_type)
var structureRestStructure[Field]

Inherited from: RemoteApi.structure

Contain things that don't vary among the model instances; ie: This is the same object and applies to all instances of a particular BaseModel class …

Methods

def delete(self)

Inherited from: RemoteApi.delete

REQUIRES associated model object [see self.model] …

def did_send(self)

Inherited from: RemoteApi.did_send

self.client will call us here after someone attempts to send us (a specific model), you and use RelationApi.model to grab the model that it happened …

def fields_to_pop_for_json(self, json: dict, field_objs: List[Field], log_output: bool) ‑> Set[Any]

Inherited from: RemoteApi.fields_to_pop_for_json

Goes through the list of fields (field_objs) to determine which ones have not changed in order to pop them out of the json representation. This method …

def forget_original_json_state(self)

Inherited from: RemoteApi.forget_original_json_state

If called, we forget/reset the orginal json state, which is a combination of all the json that this object has been updated with over it's lifetime …

def get(self, query: Dict[str, Union[str, int, datetime.date, xurls.url._FormattedQueryValue, None, Iterable[Union[str, int, datetime.date, xurls.url._FormattedQueryValue]]]] = None, *, top: int = None, fields: Optional[Sequence[str]] = Default) ‑> Optional[Iterable[~M]]

Inherited from: RemoteApi.get

Important: Right now we return a list, but it might be just a generator in the future, treat the return type as a true Iterable, something you can't …

def get_child_without_lazy_lookup(self, child_field_name, *, false_if_not_set=False) ‑> Union[BaseModel[~M], None, bool, NullType]

Inherited from: RemoteApi.get_child_without_lazy_lookup

REQUIRES associated model object [see self.model] …

def get_via_id(self, id: Union[int, str, List[Union[int, str]], Dict[str, Union[str, int]], List[Dict[str, Union[str, int]]]], fields: Sequence[str] = Default, id_field: str = None, aux_query: Dict[str, Union[str, int, datetime.date, xurls.url._FormattedQueryValue, None, Iterable[Union[str, int, datetime.date, xurls.url._FormattedQueryValue]]]] = None) ‑> Union[Iterable[~M], ~M, None]

Inherited from: RemoteApi.get_via_id

This method would have probably been better named get_via_key

def json(self, only_include_changes: bool = False, log_output: bool = False) ‑> Optional[Dict[str, Any]]

Inherited from: RemoteApi.json

BaseApi.json() to see superclass's documentation for this method …

def list_of_attrs_to_repr(self) ‑> List[str]

Inherited from: RemoteApi.list_of_attrs_to_repr

" REQUIRES associated model object [see self.model] …

def option_all_for_name(self, option_attribute_name) ‑> List[Any]

Inherited from: RemoteApi.option_all_for_name

Gets a particular option attribute by name in a particular prioritized order …

def option_for_name(self, option_attribute_name) ‑> Any

Inherited from: RemoteApi.option_for_name

Returns the first option returned from self.option_all_for_name for the option_attribute_name that is passed in; otherwise None …

def send(self, url: Union[str, URL, None] = None)

REQUIRES associated model object [see self.model].

Convenience method to send this single object to API, it simply calls xynlib.orm.base.client.Client.send_objs with a single object in the list (via xynlib.orm.base.api.BaseApi.model).

If you want to send multiple objects, call xynlib.orm.base.client.Client.send_objs.

Example is below, it uses a made-up rest model called 'SomeRestModelSubclass'.

(I did not provide all details it would need to use the made-up/imagained rest-api; trying to illisrate a basic point here is all. If you want more details on how to make a real full/valid rest-model subclass see #INSERT-README-LINK#.)

>>> from xmodel_rest import RestModel
>>> class SomeRestModelSubclass(RestModel, base_url="....etc...."):
...     pass  # Some attributes from the rest-api go here
>>> obj1 = SomeRestModelSubclass()
>>> obj2 = SomeRestModelSubclass()
>>> RestModel.api.client.send_objs([obj1, obj2])

If you pass in a url paramater to the send_objs method, the url gets appended to the final constructed url before the url gets validated.

If the url is validated, it will use that final url [with passed in url this appended]. For more information about how URL's are appended to each-other see: URLMutable.append_url().

The response from API will update all the values on this object with the results of the change [all fields will be updated] and with the latest values from API.

You can check for errors on model object via xmodel.remote.api.response_state, ie:

>>> from xynlib.orm import BaseModel
>>> obj: BaseModel
>>> # Check response_state to see if it had an error:
>>> obj.api.response_state.had_error
False
Expand source code
def send(self, url: URLStr = None):
    """ REQUIRES associated model object [see self.model].

    Convenience method to send this single object to API, it simply calls
    `xynlib.orm.base.client.Client.send_objs` with a single object in the list
    (via `xynlib.orm.base.api.BaseApi.model`).

    If you want to send multiple objects, call `xynlib.orm.base.client.Client.send_objs`.

    Example is below, it uses a made-up rest model called 'SomeRestModelSubclass'.

    (I did not provide all details it would need to use the made-up/imagained rest-api;
    trying to illisrate a basic point here is all.
    If you want more details on how to make a real full/valid rest-model subclass
    see #INSERT-README-LINK#.)

    >>> from xmodel_rest import RestModel
    >>> class SomeRestModelSubclass(RestModel, base_url="....etc...."):
    ...     pass  # Some attributes from the rest-api go here
    >>> obj1 = SomeRestModelSubclass()
    >>> obj2 = SomeRestModelSubclass()
    >>> RestModel.api.client.send_objs([obj1, obj2])

    If you pass in a `url` paramater to the `send_objs` method, the url gets appended to the
    final constructed url before the url gets validated.

    If the url is validated, it will use that final url [with passed in `url` this appended].
    For more information about how URL's are appended to each-other see:
    `xurls.url.URLMutable.append_url`.

    The response from API will update all the values on this object with the results
    of the change [all fields will be updated] and with the latest values from API.

    You can check for errors on model object via `xmodel.remote.api.response_state`, ie:

    >>> from xynlib.orm import BaseModel
    >>> obj: BaseModel
    >>> # Check response_state to see if it had an error:
    >>> obj.api.response_state.had_error
    False
    """
    # Redirect to client.send_objs:
    self.client.send_objs([self.model], url=url)
def should_include_field_in_json(self, new_value: Any, old_value: Any, field: str) ‑> bool

Inherited from: RemoteApi.should_include_field_in_json

Returns True if the the value for field should be included in the JSON. This only gets called if only_include_changes is True when passed to …

def update_from_json(self, json: Union[Dict[str, Any], Mapping[~KT, +VT_co]])

Inherited from: RemoteApi.update_from_json

BaseApi.update_from_json() to see superclass's documentation for this method …