Skip to content

Fields

The Field class is a base class that is used to create fields for objects. Fields are used to define the attributes of an object and to specify how those attributes should be validated and transformed.

Understanding the Structure

flowchart TB
    classDef orangeBorder fill:#1A1A1A,color:#E2E2E2,stroke:#FF9933,stroke-width:2px;

    EveryskBaseField(["BaseField"]):::orangeBorder
    EveryskField(["Field"]):::orangeBorder
    EveryskBaseField --> EveryskField
    EveryskField --> BoolField(["BoolField"]):::orangeBorder
    EveryskField --> StrField(["StrField"]):::orangeBorder
    EveryskField --> DateField(["DateField"]):::orangeBorder
    EveryskField --> DictField(["DictField"]):::orangeBorder
    EveryskField --> SetField(["SetField"]):::orangeBorder
    EveryskField --> FloatField(["FloatField"]):::orangeBorder
    EveryskField --> IntField(["IntField"]):::orangeBorder
    EveryskField --> ListField(["ListField"]):::orangeBorder


Let's take a look at the main methods that are available in the Field class.

Working with Attributes

The Field class comes in handy when working with field attributes, it allows us to define required, readonly, and required_lazy attributes. Let's take an individual look at them.


Attributes

All fields will inherit the following class attributes:

  • default: This attribute is used as the default value when we do not specify any value to the initialization of the class.

  • required: The required attribute is a boolean used in order to specify if a given value should be mandatory.

  • readonly: The readonly attribute is a boolean used in order to specify if a given value should not be modified after its creation.

  • required_lazy: The required_lazy attribute is a boolean used in order to specify if a given value should only be validated when the validate_required_fields() is called.

  • empty_is_none: Any empty string will be transformed to None if this attribute is set to True.

  • choices: The legacy choices attribute can be used alongside the ChoiceField class in order to make sure that a given value is contained inside a list of choices.


Understanding the Class Attributes

default

As mentioned above, the default attribute is used when we do not specify any value to the initialization of the class, take a look at the example below:

from everysk.core.object import BaseObject
from everysk.core.fields import StrField

class Test(BaseObject):
    f1 = StrField(default='hey')

test = Test()
test.f1
'hey'

test = Test(f1='bye')
test.f1
'bye'

required

As the name suggests, the boolean class attribute required states whether or not to include an attribute as mandatory. Here's how that would look like in code:

from everysk.core.object import BaseObject
from everysk.core.fields import StrField

class Test(BaseObject):
    f1 = StrField(required=True)

test = Test(f1='hey')

Now let's see what happens when we don't set any value to the f1 attribute:

test = Test()
RequiredError: The "f1" attribute is required.


readonly

As the name suggests, the readonly boolean is used when an attribute should not be modified after its creation.

from everysk.core.object import BaseObject
from everysk.core.fields import StrField
class Test(BaseObject):
    f1 = StrField(readonly=True, default='hey')

test = Test()
test.f1
'hey'

Now let's see what happens when we try to modify the f1 attribute:

test.f1 = 'bye'
FieldValueError: The field "f1" value cannot be changed.


required_lazy

The required_lazy flag, which has its default value set to False, is used in order to make sure that a given field is only validated when the validate_required_fields() is called.

from everysk.core.object import BaseObject
from everysk.core.fields import StrField

class Test(BaseObject):
    f1 = StrField(required_lazy=True)

test = Test(f1='hey')
test.validate_required_fields()

Now watch what happens when we try to call the validate_required_fields() method without setting the f1 attribute:

test = Test()
test.validate_required_fields()

RequiredError: The "f1" attribute is required.


empty_is_none

The empty_is_none flag is used to convert any empty string to None if set to True. Let's highlight below the difference between the two scenarios. Starting with the default value which is False:

from everysk.core.object import BaseObject
from everysk.core.fields import StrField
class Test(BaseObject):
    f1 = StrField(empty_is_none=False)

test = Test(f1='')
test.f1
''

As expected, we get our empty string. Now let's see what happens when we set the empty_is_none flag to True:

class Test(BaseObject):
    f1 = StrField(empty_is_none=True)

test = Test(f1='')
test.f1
None

Just for clarification, in the first example we could've omitted the empty_is_none attribute since the default value is False.


choices

There are cases where we want to make sure that a given value is actually contained inside a list of values, to achieve this we can use the following code snippet:

from everysk.core.object import BaseObject
from everysk.core.fields import ChoiceField

class Test(BaseObject):
    f1 = ChoiceField(default='blue', choices=['blue', 'red', 'green'])

test = Test()
test.f1
'blue'
test.f1 = 'red'

Now let's see what happens when we give a value that is not in the list of choices to our f1 attribute:

test.f1 = 'white'
FieldValueError: The value 'white' for field 'f1' must be in this list ['blue', 'red', 'green'].


Understanding the Methods

There are two main methods that are available in the fields.py module which are the clean_value() and validate() methods. The two of them exist from inheritance and are available in all field types, and they are also used indirectly, meaning that they are called once a value is set to a given field.


How to use the clean_value() method

The clean_value() method it is used to change the behavior of a given field. As seen in the example below, we have a custom BoolField class that will return True only if the value is equal to test. Feel free to adapt the method to your needs.

from everysk.core.fields import BoolField
from everysk.core.object import BaseObject

class MyBoolField(BoolField):
    def clean_value(self, value):
        if value == 'test':
            return True
        return False

class MyClass(BaseObject):
    f1 = MyBoolField()


a = MyClass()
a.f1 = True
a.f1
False

a.f1 = 'test'
a.f1
True


How to use the validate() method

The validate() method is used to check if a given value is valid or not. In the example below we have a custom StrField class that will raise a ValueError if the value is equal to test. Again, feel free to adapt the method to your needs.

from everysk.core.fields import StrField
from everysk.core.object import BaseObject

class MyStrField(StrField):
    def validate(self, attr_name, value):
        if value == 'test':
            raise ValueError('value argument cannot be "test"')


class MyClass(BaseObject):
    f1 = MyStrField()

a = MyClass()
a.f1 = 'test'
ValueError: value argument cannot be "test"


All the Different Field Types

Below we have a list of all the different field types that are available in the fields.py module. As mentioned before, all of them inherit from the Field class and accept the default, required, readonly, required_lazy, and empty_is_none attributes.

BoolField

A Boolean is a logical data type used to represent True or False values.

The BoolField class is used to declare class attributes and variables which will store boolean values.


DateField

Working with dates might be a tough job sometimes, but luckily the DateField has all the necessary functionalities to make sure everything works when dealing with dates.

The DateField class accepts two additional attributes: min_date and max_date. Let's see how we can make use of them:

from everysk.core.fields import DateField
from everysk.core.object import BaseObject
from everysk.core.datetime import Date

class MyObject(BaseObject):
    dt = DateField(min_date=Date(2024, 10, 1), max_date=Date(2024, 10, 31))

obj = MyObject(dt=Date(2024, 9, 23))
FieldValueError: The value "2024-09-23" for field "dt" must be between 2024-10-01 and 2024-10-24.

As seen above, we have a DateField object with the min_date and max_date attributes set. When we try to instantiate the object with a date that is not within the range, we get a FieldValueError.


DateTimeField

The DateTimeField class is fairly similar to the DateField, but now allows us to work with time units, such as hours, minutes, and seconds.

The class also accepts both min_date and max_date attributes:

from everysk.core.object import BaseObject
from everysk.core.fields import DateTimeField
from everysk.core.datetime import DateTime

class MyObject(BaseObject):
    dt = DateTimeField(min_date=DateTime(2024, 10, 31, 12, 0, 0), max_date=DateTime(2024, 10, 31, 12, 30, 0))


obj = MyObject(dt=DateTime(2024, 10, 31, 12, 31, 0))
FieldValueError: The value "2024-10-31 12:31:00+00:00" for field "dt" must be between 2024-10-31 12:00:00+00:00 and 2024-10-31 12:30:00+00:00.

The example above for the DateTimeField class is a subtle one but it helps to illustrate how the min_date and max_date attributes work.


DictField

A Python dictionary refers to a common data structure where the values are arranged in key-value pairs, in other words every key is associated with a value.

The DictField class comes with the goal of making sure all dictionaries behave the same when working with them.


EmailField

In the case we want to validate whether a piece of data corresponds to an email or not, we can use the EmailField for that.

from everysk.core.fields import EmailField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    email = EmailField(default='smith@gmail.com')

obj = MyObject()
obj.email
"smith@gmail.com"

obj.email = 'smith'
FieldValueError: Key em must be an e-mail.


FloatField

Float is a type of data represented by numbers with decimal values such as 10.5, 7.65, and 2.5. The FloatField class is designed with the purpose of facilitating the use of float values.

The class has the option to accept two additional attributes: min_size and max_size, in order to set a specific range of values that the float should have.

from everysk.core.fields import FloatField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    f1 = FloatField(min_size=1.0, max_size=10.0)

obj = MyObject(f1=5.0)
obj.f1
5.0

Now let's provide an invalid value for the f1 attribute:

obj = MyObject(f1=11.0)
FieldValueError: The value "11.0" for field "f1" must be between 1.0 and 10.0.


IntField

In computer science, integer is a data type used to represent whole numbers, numbers without a decimal point, such as 4, -9, and 15. The IntField class from the fields module is designed to deal with integer values.

The IntField class is also able to accept two additional attributes: min_size and max_size. Let's see how we can make use of them:

from everysk.core.fields import IntField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    f1 = IntField(min_size=1, max_size=10)

obj = MyObject(f1=5)
obj.f1
5

Now let's provide an invalid value for the number attribute:

obj = MyObject(f1=11)
FieldValueError: The value "11" for field "f1" must be between 1 and 10.


IteratorField

In Python, Iterable objects are capable of returning its elements one at time, a few iterator objects are lists, sets, dictionaries, tuples, and strings.

The IteratorField class provides a way of easily defining and working with iterators.


ListField

The ListField class is used to define elements which will be stored in a list. It can accept, optionally, two additional attributes: min_size and max_size. Which will serve to configure a specific range of elements that the list should have:

from everysk.core.fields import ListField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    lst = ListField(min_size=1, max_size=5)

obj = MyObject(lst=[1, 2, 3, 4, 5])
obj.lst
[1, 2, 3, 4, 5]

Let's provide an invalid list size now and see what happens:

obj = MyObject(lst=[1, 2, 3, 4, 5, 6])
FieldValueError: The attribute "lst" is not within the specified list range. min_size: 1 max_size: 5


RegexField

The RegexField class is used to store string values that correspond to regular expressions. Let's take a look at an example in practice where we need a string to start with the prefix 'data_' followed by any alphanumeric character

from everysk.core.fields import RegexField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    data = RegexField(default=r'^data_[a-zA-Z0-9]+$')

obj = MyObject(data='data_123')
obj.data
'data_123'

obj.data = 'data_123@'
FieldValueError: The value "data_123@" for field "data" must match the regular expression "^data_[a-zA-Z0-9]+$".


SetField

In Python, sets are a collection of data used to store items in a single variable. The SetField class is designed to work with sets.

There are two optional attributes that can be used with the SetField class: min_size and max_size. These attributes are used to set a specific range of elements that the set should have:

from everysk.core.fields import SetField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    st = SetField(min_size=1, max_size=5)

obj = MyObject(st={1, 2, 3, 4, 5})
obj.st
{1, 2, 3, 4, 5}

Now let's provide an invalid set size and see what happens:

obj = MyObject(st={1, 2, 3, 4, 5, 6})
FieldValueError: The attribute "st" is not within the specified set range. min_size: 1 max_size: 5


StrField

Strings are array of characters used to represent text. The StrField class is used to define and store string values. Apart from the default attributes which are inherited from the BaseField class.

The StrField class accepts two additional attributes: min_size and max_size. These attributes are used to set a specific range of characters that the string should have:

from everysk.core.fields import StrField
from everysk.core.object import BaseObject

class MyObject(BaseObject):
    f1 = StrField(min_size=1, max_size=4)

obj = MyObject(f1='test')
obj.f1
'test'

Let's provide an invalid string size now and see what happens:

obj = MyObject(f1='tests')
FieldValueError: The length "5" for attribute "f1" must be between "1" and "4".

The StrField class also accept a regex attribute:

from everysk.core.fields import StrField
from everysk.core.object import BaseObject

import re

class MyObject(BaseObject):
    name = StrField(regex=re.compile(r'^[a-zA-Z]+$'))

obj = MyObject(name='John')
obj.name
'John'

obj.name = 'John1'
FieldValueError: The value "John1" for field "name" must match the regular expression "^[a-zA-Z]+$".


TupleField

The TupleField class from the fields module is used to define and store tuple values.

from everysk.core.object import BaseObject
from everysk.core.fields import TupleField

class MyObject(BaseObject):
    name = TupleField(default=('a', 'b', 'c'), readonly=True)

test = MyObject()
test.name
('a', 'b', 'c')


URLField

The URLField class is designed with the goal of storing and validating multiple URL patterns by checking the string against common protocols such as HTTP, HTTPS, FTP, and FTPS.

from everysk.core.object import BaseObject
from everysk.core.fields import URLField

class MyObject(BaseObject):
    url = URLField()

test = MyObject()
test.url = 'abc'
FieldValueError: Key url must be an URL.

test.url = 'https://example.com'
test.url
'https://example.com'