Field reference

This page lists all the available field types to use in models.

Hint

Some examples on this page return filter objects. These should be used when filtering results, for example when calling find().

Scalar fields

Scalar fields are used where values from the Sugar backend can be directly mapped to some Python equivalent without any side effects. That means that these values can also be serialized and deserialized without using a model at all. Further, scalar fields mostly don’t depend on any state or other records.

This field type is mainly used for primitive data types. All of them inherit from this common base class and share a few options for filtering:

class zucker.model.fields.base.ScalarField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Scalar fields are fields that also support some basic filtering operations.

Parameters:

validators – Validators can be provided to make sure that data meets specific requirements. These will be checked both for incoming values from the server and for any value that is set on the field, before writing changes. A validator may either be a function or a regular expression object. The former will be called with the value to be checked as the single argument and should raise a ValueError when validation fails. The latter will pass the check if the entire value matches the provided regular expression.

Note

A few notes to keep in mind when using validators:

  1. The default strategy for validating regular expressions will coerce the incoming type to a string. That means that – for example – the number 0xff will match the expression 2.., because the string representation is 255.

  2. Validators are always evaluated on the api data type. That means that they are run after serializing any user input.

__eq__(other: Optional[Union[NativeType, ApiType]]) NegatableFilter[Any]

Filter for exact values of this field.

Depending on type of the given value, this is is equivalent to one of the other filtering methods:

>>> Person.name == "Ben" # Is the same as Person.name.values("Ben")
>>> Person.age == 3 # Is the same as Person.age.values(3)
>>> Person.supervisor == None # Is the same as Person.supervisor.null()
__ne__(other: Optional[Union[NativeType, ApiType]]) NegatableFilter[Any]

Inverse of the == filter operator.

Use the != operator to exclude specific values:

>>> Person.name != "Ben" # Is the same as ~(Person.name.values("Ben"))
>>> Person.supervisor != None # Is the same as ~(Person.supervisor.null())
null() NullishFilter

Filter for whether the field is null.

Use the filter like this:

>>> Person.employer.null()

This will return objects where the ‘employer’ field is not set.

To find only objects where a field is explicitly not null, invert the filter:

>>> ~Person.employer.null()

As a shorthand, you can also use the equals operator for the above examples:

>>> Person.employer == None
>>> Person.employer != None
values(*values: Union[NativeType, ApiType]) ValuesFilter[ApiType]

Filter for exact values of this field.

Most basic use for this filter is finding objects by value. The filter

>>> Person.name.values("Ben")

will return objects who’s name is ‘Ben’.

This filter takes one or more arguments. It matches entries where this set directly contains the field’s value, for example:

>>> Person.name.values("Paul", "Spencer")

will match objects who’s name is either ‘Paul’ or ‘Spencer’.

Inverting this filter yields a ‘not-equal’ filter, for example:

>>> ~Person.name.values("Mike")

This query will match all objects where the name is not equal to ‘Mike’.

The above examples are also available as a shorthand through the equals operator (although you can only check for a single value here):

>>> Person.name == "Ben"
>>> Person.name != "Ben"

Strings

For text data, use a string field:

class zucker.model.StringField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable field that handles the various string columns.

This will handle any backend field that has the database type varchar, text, text, encrypt, longtext or textarea.

contains(infix: str) StringFilter

Filter for values that contain a given string.

ends_with(suffix: str) StringFilter

Filter for values that end with a given string.

not_empty() NotEmptyFilter

Filter for non-empty values.

starts_with(prefix: str) StringFilter

Filter for values that start with a given string.

Booleans

Boolean fields store either True or False:

class zucker.model.BooleanField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable field for boolean columns.

false() ValuesFilter[bool]

Filter for false values.

true() ValuesFilter[bool]

Filter for true values.

Numbers

Depending on the type of number stored, use one of these two fields:

class zucker.model.IntegerField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable field for integer columns.

Use this for backend fields of type int, integer, long, smallint, tinyint or ulong.

class zucker.model.FloatField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable field for floating-point number columns.

This is appropriate for backend fields that have the type float or decimal.

Both of these fields share an API for filtering:

class zucker.model.fields.base.NumericField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Scalar field with filtering operators that produce a total ordering.

__gt__(other: Any) NumericFilter

Filter for values less than the specified scalar:

>>> Person.age > 60
__gte__(other: Any) NumericFilter

Filter for values greater than or equal to the specified scalar:

>>> Person.age >= 21
__lt__(other: Any) NumericFilter

Filter for values less than the specified scalar:

>>> Person.age < 10
__lte__(other: Any) NumericFilter

Filter for values less than or equal to the specified scalar:

>>> Person.age <= 18

URLs

URLs can be accessed with this field:

class zucker.model.URLField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable string field for URLs.

This should be used for database columns of type url. URLs will be decoded and returned as an instance of urllib.parse.ParseResult, which contains the parsed components of the URL. To get a string, call str() with the result object.

Emails

Sugar exposes emails with two APIs: a legacy version and a new version. The new version supports connecting any number of emails to a record and treats them like a link. This also allows setting metadata on emails, for example to opt out of communication. The legacy version exposes two fields email1 and email2 on the record which map to the first and second email provided through the first method. See the documentation for more details.

Zucker currently only supports the legacy API through this field:

class zucker.model.LegacyEmailField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Field for legacy email addresses (email1 and email2).

This is a string field that also validates [1] any input to be email-like. It should only be used on string columns with names email1 and email2.

Normally, this field is used like this (with the second instance being optional):

class Lead(model.UnboundModule):
    email1 = model.LegacyEmailField()
    email2 = model.LegacyEmailField()

Enumerations

The Sugar Studio allows to define fields of a type called Dropdown. This field type allows users to select exactly one out of a predefined set of values. Zucker maps these fields to Python’s enum types:

class zucker.model.EnumField(enum: Type[EnumType], /, api_name: Optional[str] = None, **kwargs: Any)

Mutable field that represents Dropdown Sugar fields.

Parameters:

enum – Pass an enum.Enum type that represents the options for this field. This enum must have a non-null member named DEFAULT. Further, elements should be strings (unless otherwise specified on the server side).

Define an enum and use the field like this:

import enum

class LeadSource(enum.Enum):
  DEFAULT = ""
  OTHER = "Other"
  EXISTING_CUSTOMER = "Existing Customer"
  DIRECT_MAIL = "Direct Mail"
  COLD_CALL = "Cold Call"
  WORD_OF_MOUTH = "Word of mouth"
  # ...

class Lead(model.UnboundModule):
    lead_source = model.EnumField(LeadSource)

Again, note the DEFAULT field.

Note

In the metadata API, Sugar represents these fields with the additional parameter options. Other than that, enumerations are regular text fields.

IDs

Record IDs can be accessed using fields of this type:

class zucker.model.IdField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Immutable field for accessing record IDs.

An instance of this field is automatically created for every module. Manually creating fields of this type should only be necessary for referencing links. In Python, IDs are represented by UUID objects.

Note that modules automatically receive a field of this type with the name id.

Relationships

Sugar offers a number of ways to model bidirectional data relationships. The main way is by using relationship links. Here, a link is defined between two module types and both models get a corresponding field. In Zucker, use this field to access a link:

class zucker.model.RelatedField(related_module: Union[Type[SyncModule], Type[AsyncModule]], link_name: str)

Field that returns a view on a relationship link.

Parameters:
  • related_module – The module type on the other side of the relationship.

  • link_name – Name of the link.

Note

The related module used to initialize this field must be a bound module with the same client as the module the field is attached to. You can’t mix synchronous and asynchronous models here.

Implementing fields

Next to the already mentioned ScalarField and NumericField, there are also other base classes available to use when implementing custom field types. In fact, most of the above scalar fields above actually implement the mutable variants:

class zucker.model.fields.base.MutableScalarField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable version of ScalarField.

class zucker.model.fields.base.MutableNumericField(api_name: Optional[str] = None, *, validators: Optional[Sequence[Union[Pattern[str], Callable[[ApiType], None]]]] = None)

Mutable version of NumericField.

A field being mutable means that when a field name is used on a Record record type, both of the following will work:

the_name = record.name  # Get the name of a record object
record.name = "New Name"  # Set a new value

To implement a scalar field, choose and applicable superclass and override it. Scalar fields require two generic arguments - native type and an API type. The latter is the type of data the Sugar API returns for the field and should be a JSON type. Normally, this will be one of the JSON-native scalars str, int, float or bool. The native type may be the same, but can also be something different. This is the rich Python type that the API response gets converted into. A typical example here is for dates, which are typically encoded as strings and returned as native datetime objects.

To handle this conversion, you will need to implement these two methods on the new field class:

abstract ScalarField.load_value(raw_value: JsonType) NativeType

Load a value from the API into a native data type.

Parameters:

raw_value – Response from the API for this field. This will be a JSON primitive, which should either be returned as-is (where appropriate) or converted into a native Python data type.

Returns:

A Python data type for this field.

abstract ScalarField.serialize(value: Union[NativeType, ApiType]) ApiType

Serialize a native data type into something the API can take back for saving.

This method also supports “serializing” api types. In this case implementors are advised to verify the input’s validity and return it as-is.

Parameters:

value – Native or API data type for this field.

Returns:

An API-compatible data type.

Note

When setting the value of a mutable field, you can provide both the native as well as the API type. For a hypothetical BirdField field on a Zoo model, this would both be possible:

>>> the_bird = zoo.bird
>>> the_bird
<Bird object at ...>
>>> zoo.bird = the_bird  # Set the field value using the native data type
>>> zoo.bird = "zazu"  # Set the field value using the API type (assuming birds serialize to strings)

This is why the serialize() takes both input types.

Numeric fields only have one generic argument, as the native and API types are assumed to be the same.