Skip to content

BaseField class

edgy.core.db.fields.base.BaseField

BaseField(*, default=Undefined, server_default=Undefined, **kwargs)

Bases: BaseFieldType, FieldInfo

The base field for Edgy data model fields. It provides some helpers additional to BaseFieldType and inherits from FieldInfo for pydantic integration.

Allows factories to overwrite methods.

Source code in edgy/core/db/fields/base.py
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
def __init__(
    self,
    *,
    default: Any = Undefined,
    server_default: Any = Undefined,
    **kwargs: Any,
) -> None:
    if "__type__" in kwargs:
        kwargs["field_type"] = kwargs.pop("__type__")
    self.explicit_none = default is None
    self.server_default = server_default

    super().__init__(**kwargs)

    # set remaining attributes
    for name, value in kwargs.items():
        setattr(self, name, value)

    # null is used for nullable columns and is_required is False
    # this is required for backward compatibility and pydantic_core uses null=True too
    # for opting out of nullable columns overwrite the get_column(s) method
    if (
        self.null
        or (self.server_default is not None and self.server_default is not Undefined)
        or self.autoincrement
    ) and default is Undefined:
        default = None
    if default is not Undefined:
        self.default = default

owner instance-attribute

owner

operator_mapping class-attribute instance-attribute

operator_mapping = {'is': 'is_', 'in': 'in_', 'exact': '__eq__', 'not': '__ne__', 'gt': '__gt__', 'ge': '__ge__', 'gte': '__ge__', 'lt': '__lt__', 'lte': '__le__', 'le': '__le__'}

auto_compute_server_default class-attribute instance-attribute

auto_compute_server_default = False

explicit_none instance-attribute

explicit_none = default is None

server_default instance-attribute

server_default = server_default

default instance-attribute

default = default

no_copy class-attribute instance-attribute

no_copy = False

read_only class-attribute instance-attribute

read_only = False

inject_default_on_partial_update class-attribute instance-attribute

inject_default_on_partial_update = False

inherit class-attribute instance-attribute

inherit = True

skip_absorption_check class-attribute instance-attribute

skip_absorption_check = False

skip_reflection_type_check class-attribute instance-attribute

skip_reflection_type_check = False

field_type class-attribute instance-attribute

field_type = Any

factory class-attribute instance-attribute

factory = None

__original_type__ class-attribute instance-attribute

__original_type__ = None

name class-attribute instance-attribute

name = ''

secret class-attribute instance-attribute

secret = False

exclude class-attribute instance-attribute

exclude = False

primary_key class-attribute instance-attribute

primary_key = False

autoincrement class-attribute instance-attribute

autoincrement = False

null class-attribute instance-attribute

null = False

index class-attribute instance-attribute

index = False

unique class-attribute instance-attribute

unique = False

registry property

registry

get_server_default

get_server_default()

Retrieve the server_default.

Source code in edgy/core/db/fields/base.py
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
def get_server_default(self) -> Any:
    """
    Retrieve the server_default.
    """
    server_default = getattr(self, "server_default", Undefined)
    # check if there was an explicit defined server_default
    if server_default is not Undefined:
        return server_default
    default = self.default
    if default is Undefined or default is None:
        return None
    if not callable(default):
        if self.auto_compute_server_default is None:
            auto_compute_server_default: bool = (
                not self.null and settings.allow_auto_compute_server_defaults
            )
        elif self.auto_compute_server_default == "ignore_null":
            auto_compute_server_default = settings.allow_auto_compute_server_defaults
        else:
            auto_compute_server_default = self.auto_compute_server_default
    else:
        auto_compute_server_default = self.auto_compute_server_default is True

    if auto_compute_server_default:
        return self.customize_default_for_server_default(default)
    return None

customize_default_for_server_default

customize_default_for_server_default(default)

Modify default for non-null server_default.

PARAMETER DESCRIPTION
default

The original default.

TYPE: Any

Source code in edgy/core/db/fields/base.py
122
123
124
125
126
127
128
129
130
131
132
def customize_default_for_server_default(self, default: Any) -> Any:
    """
    Modify default for non-null server_default.

    Args:
        default: The original default.

    """
    if callable(default):
        default = default()
    return sqlalchemy.text(":value").bindparams(value=default)

get_columns_nullable

get_columns_nullable()

Helper method. Returns if the columns of the field should be nullable.

Source code in edgy/core/db/fields/base.py
134
135
136
137
138
139
140
141
142
def get_columns_nullable(self) -> bool:
    """
    Helper method.
    Returns if the columns of the field should be nullable.
    """
    if self.null:
        return True
    force_fields = FORCE_FIELDS_NULLABLE.get()
    return (self.owner.__name__, self.name) in force_fields or ("", self.name) in force_fields

operator_to_clause

operator_to_clause(field_name, operator, table, value)

Base implementation, adaptable

Source code in edgy/core/db/fields/base.py
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
def operator_to_clause(
    self, field_name: str, operator: str, table: sqlalchemy.Table, value: Any
) -> Any:
    """Base implementation, adaptable"""
    # Map the operation code onto SQLAlchemy's ColumnElement
    # https://docs.sqlalchemy.org/en/latest/core/sqlelement.html#sqlalchemy.sql.expression.ColumnElement
    # MUST raise an KeyError on missing columns, this code is used for the generic case if no field is available
    column = table.columns[field_name]
    operator = self.operator_mapping.get(operator, operator)
    if operator == "iexact":
        ESCAPE_CHARACTERS = ["%", "_"]
        has_escaped_character = any(c for c in ESCAPE_CHARACTERS if c in value)
        if has_escaped_character:
            value = value.replace("\\", "\\\\")
            # enable escape modifier
            for char in ESCAPE_CHARACTERS:
                value = value.replace(char, f"\\{char}")
        clause = column.ilike(value, escape="\\" if has_escaped_character else None)
        return clause
    elif operator in {
        "contains",
        "icontains",
        "startswith",
        "endswith",
        "istartswith",
        "iendswith",
    }:
        return getattr(column, operator)(value, autoescape=True)
    return getattr(column, operator)(value)

is_required

is_required()

Check if the argument is required.

RETURNS DESCRIPTION
bool

True if the argument is required, False otherwise.

Source code in edgy/core/db/fields/base.py
174
175
176
177
178
179
180
181
182
183
184
185
186
187
def is_required(self) -> bool:
    """Check if the argument is required.

    Returns:
        `True` if the argument is required, `False` otherwise.
    """
    if self.primary_key and self.autoincrement:
        return False
    return not (
        self.null
        # we cannot use get_server_default() here. The test is client side not for migrations.
        or (self.server_default is not None and self.server_default is not Undefined)
        or self.has_default()
    )

has_default

has_default()

Checks if the field has a default value set

Source code in edgy/core/db/fields/base.py
189
190
191
192
193
def has_default(self) -> bool:
    """Checks if the field has a default value set"""
    return bool(
        (self.default is not None or self.explicit_none) and self.default is not Undefined
    )

get_columns

get_columns(name)

Returns the columns of the field being declared.

Source code in edgy/core/db/fields/base.py
195
196
197
198
199
def get_columns(self, name: str) -> Sequence[sqlalchemy.Column]:
    """
    Returns the columns of the field being declared.
    """
    return []

embed_field

embed_field(prefix, new_fieldname, owner=None, parent=None)

Embed this field or return None to prevent embedding. Must return a copy with name and owner set when not returning None.

Source code in edgy/core/db/fields/base.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
def embed_field(
    self,
    prefix: str,
    new_fieldname: str,
    owner: Optional[type["BaseModelType"]] = None,
    parent: Optional["BaseFieldType"] = None,
) -> Optional["BaseField"]:
    """
    Embed this field or return None to prevent embedding.
    Must return a copy with name and owner set when not returning None.
    """
    field_copy = copy.copy(self)
    field_copy.name = new_fieldname
    field_copy.owner = owner
    if getattr(parent, "prefix_column_name", None):
        if getattr(field_copy, "column_name", None):
            field_copy.column_name = f"{parent.prefix_column_name}{field_copy.column_name}"  # type: ignore
        else:
            field_copy.column_name = (
                f"{parent.prefix_column_name}{new_fieldname[len(prefix) :]}"  # type: ignore
            )

    return field_copy

get_default_value

get_default_value()
Source code in edgy/core/db/fields/base.py
225
226
227
228
229
230
def get_default_value(self) -> Any:
    # single default
    default = getattr(self, "default", None)
    if callable(default):
        return default()
    return default

get_default_values

get_default_values(field_name, cleaned_data)
Source code in edgy/core/db/fields/base.py
232
233
234
235
236
237
238
239
def get_default_values(self, field_name: str, cleaned_data: dict[str, Any]) -> dict[str, Any]:
    # for multidefaults overwrite in subclasses get_default_values to
    # parse default values differently
    # NOTE: multi value fields should always check here if defaults were already applied
    # NOTE: when build meta fields without columns this should be empty
    if field_name in cleaned_data:
        return {}
    return {field_name: self.get_default_value()}

clean

clean(field_name, value, for_query=False)

Validates a value and transform it into columns which can be used for querying and saving.

PARAMETER DESCRIPTION
field_name

the field name (can be different from name)

TYPE: str

value

the field value

TYPE: Any

Kwargs: for_query: Is used for querying. Should have all columns used for querying set. The columns used can differ especially for multi column fields.

Source code in edgy/core/db/fields/types.py
112
113
114
115
116
117
118
119
120
121
122
123
def clean(self, field_name: str, value: Any, for_query: bool = False) -> dict[str, Any]:
    """
    Validates a value and transform it into columns which can be used for querying and saving.

    Args:
        field_name: the field name (can be different from name)
        value: the field value
    Kwargs:
        for_query: Is used for querying. Should have all columns used for querying set.
                   The columns used can differ especially for multi column fields.
    """
    return {}

to_model

to_model(field_name, value)

Inverse of clean. Transforms column(s) to a field for edgy.Model. Validation happens later.

PARAMETER DESCRIPTION
field_name

the field name (can be different from name)

TYPE: str

value

the field value

TYPE: Any

Source code in edgy/core/db/fields/types.py
125
126
127
128
129
130
131
132
133
134
135
136
137
138
def to_model(
    self,
    field_name: str,
    value: Any,
) -> dict[str, Any]:
    """
    Inverse of clean. Transforms column(s) to a field for edgy.Model.
    Validation happens later.

    Args:
        field_name: the field name (can be different from name)
        value: the field value
    """
    return {field_name: value}

get_global_constraints

get_global_constraints(name, columns, schemes=())

Return global constraints and indexes. Useful for multicolumn fields

Source code in edgy/core/db/fields/types.py
140
141
142
143
144
145
146
147
148
149
def get_global_constraints(
    self,
    name: str,
    columns: Sequence[sqlalchemy.Column],
    schemes: Sequence[str] = (),
) -> Sequence[Union[sqlalchemy.Constraint, sqlalchemy.Index]]:
    """Return global constraints and indexes.
    Useful for multicolumn fields
    """
    return []

get_embedded_fields

get_embedded_fields(field_name, fields)

Define extra fields on the fly. Often no owner is available yet.

PARAMETER DESCRIPTION
field_name

the field name (can be different from name)

TYPE: str

fields

the existing fields

TYPE: dict[str, BaseFieldType]

the returned fields are changed after return, so you should

return new fields or copies. Also set the owner of the field to them before returning

Source code in edgy/core/db/fields/types.py
151
152
153
154
155
156
157
158
159
160
161
162
163
164
def get_embedded_fields(
    self, field_name: str, fields: dict[str, BaseFieldType]
) -> dict[str, BaseFieldType]:
    """
    Define extra fields on the fly. Often no owner is available yet.

    Args:
        field_name: the field name (can be different from name)
        fields: the existing fields

    Note: the returned fields are changed after return, so you should
          return new fields or copies. Also set the owner of the field to them before returning
    """
    return {}

get_column_names

get_column_names(name='')
Source code in edgy/core/db/fields/types.py
207
208
209
210
def get_column_names(self, name: str = "") -> frozenset[str]:
    if name:
        return cast("MetaInfo", self.owner.meta).field_to_column_names[name]
    return cast("MetaInfo", self.owner.meta).field_to_column_names[self.name]