Module xdynamo.fields

Global variables

var Hash

Used in type-hints to wrap other type-hints to declare that it could be that type or Null value. Indicates that a value of Null could be assigned.

You can use it like so:

>>> from null_type import Nullable, Null
>>>
>>> nullable_string: Nullable[str]
>>>
>>> # You can assign a <code>str</code> or a <code>Null</code> and it will be type-correct:
>>> nullable_string = "hello!"
>>> nullable_string = Null

Classes

class DynField (name: str = Default, type_hint: Type = <property object>, nullable: bool = Default, read_only: bool = Default, exclude: bool = Default, default: Any = Default, post_filter: Optional[xmodel.base.fields.Filter] = Default, converter: Optional[xmodel.base.fields.Converter] = Default, fget: Optional[Callable[[M], Any]] = Default, fset: Optional[Callable[[BaseModel, Any], None]] = Default, include_with_fields: Set[str] = Default, json_path: str = Default, json_path_separator: str = Default, include_in_repr: bool = Default, related_type: Optional[Type[BaseModel]] = Default, related_field_name_for_id: Optional[str] = Default, related_to_many: bool = Default, model: BaseModel = Default)

If this is not used on a model field/attribute, the field will get the default set of options automatically if the field has a type-hint; see topic BaseModel Fields.

Preferred way going forward to provide additional options/configuration to BaseModel fields.

If you don't specify a value for a particular attribute, it will have the xsentinels.default.Default value. When a Default value is encountered while constructing a xmodel.base.model.BaseModel, it will resolve these Default values and assign the final value for the field.

To resolve these Defaults, it will look at field on the parent BaseModel class. If a non-Default value is defined there, it will use that for the child. If not, then it looks at the next parent. If no non-Default value is found we then use a value that makes sense. You can see what this is in the first line of each doc-comment. In the future, when we start using Python 3.9 we can use type annotations (typing.Annotated) to annotate a specific value to the Default type generically. For now it's hard-coded.

Side Notes

Keep in mind that after the .api is accessed for the first time on a particular model class, the sdk will construct the rest of the class (lazily)… it will read and then remove/delete from the BaseModel class any type-hinted json fields with a Field object assigned to the class. It moves these Field objects into a special internal structure. The class gets None values set on all fields after this is done.

Details on why we remove them:

Doing this helps with getattr, as it will still be executed for fields without a value when we create an object instance. getattr is used to support lazy lookups [via API] of related objects. Using getattr is much faster than using the getattribute version. So I want to keep using the getattr version if possible.

Expand source code
class DynField(Field):
    dyn_key: Optional[DynKeyType] = Default

    def resolve_defaults(
            self,
            name,
            type_hint: Type,
            default_converter_map: Optional[Dict[Type, Converter]] = None,
            parent_field: "DynField" = None
    ):
        # pydoc3 will copy the parent-class doc-comment if left empty here;
        # that's exactly what I want so leaving doc-comment blank.
        super().resolve_defaults(
            name=name,
            type_hint=type_hint,
            default_converter_map=default_converter_map,
            parent_field=parent_field
        )

        if self.dyn_key:
            if not self.was_option_explicitly_set_by_user('include_in_repr'):
                self.include_in_repr = True

Ancestors

  • xmodel.base.fields.Field

Subclasses

Class variables

var dyn_key : Optional[DynKeyType]

Methods

def resolve_defaults(self, name, type_hint: Type, default_converter_map: Optional[Dict[Type, xmodel.base.fields.Converter]] = None, parent_field: DynField = None)

Resolves all dataclass attributes/fields on self that are still set to Default. The only exception is type_hint. We will always use what is passed in, regardless of if there is a parent-field with one set. This allows one on a BaseModel to easily override the type-hint without having to create a field with an explicitly set type_hint set on it (ie: let normal python annotated type-hint override any parent type).

This includes ones on subclasses [dataclass will generically tell us about all of them]. System calls this when a BaseModel class is being lazily constructed [ie: when gets the xmodel.base.model.BaseModel.api attribute for the first time or attempts to create an instance of the BaseModel for the fist time].

When the BaseModel class is being constructed, this method is called to resolve all the Default values still on the instance. We do this by:

  1. We first look at parent_field object if one has been given.
    • If ask that parent field which options where explicitly set by user and which ones were set by resolving a xsentinels.default.Default. Field objects have an internal/private var that keeps track of this.
  2. Next, figure out standard default value for option if option's current value is current at xsentinels.default.Default (a default sentential value, used to detect which values were left unset by user).

More Details

I have Field objects keep track of which fields were not at Default when they are resolved. This allows child Field objects to know which values to copy into themselves and which ones should be resolved normally via Default.

The goal here is to avoid copying value from Parent that were originally resolved via Default mechanism (and were not set explicitly by user).

An example of why this is handy:

If we have a parent model with a field of a different type vs the one on the child. Unless the converter was explicitly set by the user we want to just use the default converter for the different type on the child (and not use the wrong converter by default).

class HashField (name: str = Default, type_hint: Type = <property object>, nullable: bool = Default, read_only: bool = Default, exclude: bool = Default, default: Any = Default, post_filter: Optional[xmodel.base.fields.Filter] = Default, converter: Optional[xmodel.base.fields.Converter] = Default, fget: Optional[Callable[[M], Any]] = Default, fset: Optional[Callable[[BaseModel, Any], None]] = Default, include_with_fields: Set[str] = Default, json_path: str = Default, json_path_separator: str = Default, include_in_repr: bool = Default, related_type: Optional[Type[BaseModel]] = Default, related_field_name_for_id: Optional[str] = Default, related_to_many: bool = Default, model: BaseModel = Default)

If this is not used on a model field/attribute, the field will get the default set of options automatically if the field has a type-hint; see topic BaseModel Fields.

Preferred way going forward to provide additional options/configuration to BaseModel fields.

If you don't specify a value for a particular attribute, it will have the xsentinels.default.Default value. When a Default value is encountered while constructing a xmodel.base.model.BaseModel, it will resolve these Default values and assign the final value for the field.

To resolve these Defaults, it will look at field on the parent BaseModel class. If a non-Default value is defined there, it will use that for the child. If not, then it looks at the next parent. If no non-Default value is found we then use a value that makes sense. You can see what this is in the first line of each doc-comment. In the future, when we start using Python 3.9 we can use type annotations (typing.Annotated) to annotate a specific value to the Default type generically. For now it's hard-coded.

Side Notes

Keep in mind that after the .api is accessed for the first time on a particular model class, the sdk will construct the rest of the class (lazily)… it will read and then remove/delete from the BaseModel class any type-hinted json fields with a Field object assigned to the class. It moves these Field objects into a special internal structure. The class gets None values set on all fields after this is done.

Details on why we remove them:

Doing this helps with getattr, as it will still be executed for fields without a value when we create an object instance. getattr is used to support lazy lookups [via API] of related objects. Using getattr is much faster than using the getattribute version. So I want to keep using the getattr version if possible.

Expand source code
class HashField(DynField):
    dyn_key = DynKeyType.hash

Ancestors

Class variables

var dyn_key

Methods

def resolve_defaults(self, name, type_hint: Type, default_converter_map: Optional[Dict[Type, xmodel.base.fields.Converter]] = None, parent_field: DynField = None)

Inherited from: DynField.resolve_defaults

Resolves all dataclass attributes/fields on self that are still set to Default. The only exception is type_hint. We will always use what is passed …

class RangeField (name: str = Default, type_hint: Type = <property object>, nullable: bool = Default, read_only: bool = Default, exclude: bool = Default, default: Any = Default, post_filter: Optional[xmodel.base.fields.Filter] = Default, converter: Optional[xmodel.base.fields.Converter] = Default, fget: Optional[Callable[[M], Any]] = Default, fset: Optional[Callable[[BaseModel, Any], None]] = Default, include_with_fields: Set[str] = Default, json_path: str = Default, json_path_separator: str = Default, include_in_repr: bool = Default, related_type: Optional[Type[BaseModel]] = Default, related_field_name_for_id: Optional[str] = Default, related_to_many: bool = Default, model: BaseModel = Default)

If this is not used on a model field/attribute, the field will get the default set of options automatically if the field has a type-hint; see topic BaseModel Fields.

Preferred way going forward to provide additional options/configuration to BaseModel fields.

If you don't specify a value for a particular attribute, it will have the xsentinels.default.Default value. When a Default value is encountered while constructing a xmodel.base.model.BaseModel, it will resolve these Default values and assign the final value for the field.

To resolve these Defaults, it will look at field on the parent BaseModel class. If a non-Default value is defined there, it will use that for the child. If not, then it looks at the next parent. If no non-Default value is found we then use a value that makes sense. You can see what this is in the first line of each doc-comment. In the future, when we start using Python 3.9 we can use type annotations (typing.Annotated) to annotate a specific value to the Default type generically. For now it's hard-coded.

Side Notes

Keep in mind that after the .api is accessed for the first time on a particular model class, the sdk will construct the rest of the class (lazily)… it will read and then remove/delete from the BaseModel class any type-hinted json fields with a Field object assigned to the class. It moves these Field objects into a special internal structure. The class gets None values set on all fields after this is done.

Details on why we remove them:

Doing this helps with getattr, as it will still be executed for fields without a value when we create an object instance. getattr is used to support lazy lookups [via API] of related objects. Using getattr is much faster than using the getattribute version. So I want to keep using the getattr version if possible.

Expand source code
class RangeField(DynField):
    dyn_key = DynKeyType.range

Ancestors

Class variables

var dyn_key

Methods

def resolve_defaults(self, name, type_hint: Type, default_converter_map: Optional[Dict[Type, xmodel.base.fields.Converter]] = None, parent_field: DynField = None)

Inherited from: DynField.resolve_defaults

Resolves all dataclass attributes/fields on self that are still set to Default. The only exception is type_hint. We will always use what is passed …