Python Engine
Supported Versions
We currently support Python 3.7 and newer. It's the lowest version that contains both typing and datetime functions.
We are open to support lower python versions based on user feedback.
Data Types
Standard
Design | Python | Default Value |
---|---|---|
bool | boole | False |
int | int | 0 |
int8 | int | 0 |
int16 | int | 0 |
int32 | int | 0 |
int64 | int | 0 |
uint | int | 0 |
uint8 | int | 0 |
uint16 | int | 0 |
uint32 | int | 0 |
uint64 | int | 0 |
float | float | 0.0 |
double | float | 0.0 |
string | str | '' |
char | str | '' |
byte | int | 0 |
bytea | bytes | None |
date | datetime | None |
datetime | datetime | None |
Complex
Name | Default Value |
---|---|
Array | [] |
Object (Map) | {} |
Config
There's currently no python config options available for this engine. It's likely to change in the future based on user feedback.
Typing
OneGen takes advantage of the typing library to make your models more predictable and easier to work with.
Optionals
Optional[X] is a type that's part the typing library. It contains either the underlying value, or None.
Let's say you decide one of your model properties should be an optional string (string?). Such property in Python engine would be generated as Optional[str].
Enum
We use Python's Enum class as shown in an example below.
from enum import Enum
class Color(Enum):
GREEN = "GREEN"
BLUE = "BLUE"
BROWN = "BROWN"
eye_color = Color.BROWN
print(eye_color) # prints out 'Color.BROWN'
# To create Color enum from raw value:
car_color = Color("BLUE")
print(car_color) # prints out 'Color.BLUE'
Class
Every class is generated with all of its properties set to a default value and a constructor that accepts **kwargs
. See an example User class below.
class User:
email: str = ''
password: str = ''
age: int = 0
def __init__(self, **kwargs) -> None:
if 'email' in kwargs:
self.email = kwargs['email']
if 'password' in kwargs:
self.password = kwargs['password']
if 'age' in kwargs:
self.age = kwargs['age']
# Usage
if __name__ == "__main__":
u = User(email="john@doe.com")
print(u.email) # prints john@doe.com
Serialization
OneGen generates serialization (to_dict) and deserialization (from_dict) methods for every class. These methods make serialization a breeze.
Let's consider the following example. We have two models, User and Company (which is referenced from the User model). Take a look at the generated serialization methods and how they work.
# File: models/company.py
class Company:
name: str = ''
def __init__(self, **kwargs) -> None:
if 'name' in kwargs:
self.name = kwargs['name']
def to_dict(self) -> dict:
result: dict = {}
result['name'] = self.name
return result
@staticmethod
def from_dict(obj: dict) -> 'Company':
model = Company()
model.name = obj['name']
return model
# File: models/user.py
import datetime
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from .company import Company
class User:
email: str = ''
password: str = ''
is_subscribed: bool = False
created: datetime = None
company: 'Company' = None
def __init__(self, **kwargs) -> None:
if 'email' in kwargs:
self.email = kwargs['email']
if 'password' in kwargs:
self.password = kwargs['password']
if 'is_subscribed' in kwargs:
self.is_subscribed = kwargs['is_subscribed']
if 'created' in kwargs:
self.created = kwargs['created']
if 'company' in kwargs:
self.company = kwargs['company']
def to_dict(self) -> dict:
result: dict = {}
result['email'] = self.email
result['password'] = self.password
result['isSubscribed'] = self.is_subscribed
result['created'] = self.created.isoformat() if self.created else None
result['company'] = self.company.to_dict() if self.company else None
return result
@staticmethod
def from_dict(obj: dict) -> 'User':
from .company import Company
model = User()
model.email = obj['email']
model.password = obj['password']
model.is_subscribed = obj['isSubscribed']
model.created = datetime.datetime.fromisoformat(obj['created']) if obj['created'] else None
model.company = Company.from_dict(obj['company']) if obj['company'] else None
return model
from_dict (deserialize)
This static method takes a Python dictionary and returns a new instance of the associated model. It's very easy to use, let's say we want to create a User object from a mocked API response.
# File main.py
from models.user import User
if __name__ == "__main__":
# Imagine the following object is returned from your API
userAPIResponse = {
"email": "joe@doe.com",
"password": "",
"isSubscribed": True,
"created": "2022-05-30T21:26:49+00:00",
"company": {"name": "Friendly Corp"} # Notice the plain Company object here
}
user = User.from_dict(userAPIResponse)
print(user.email) # prints out 'joe@doe.com'
print(user.company) # it's an instance of Company now
print(user.created) # it's an instance of Date now
to_dict (serialize)
This instance method doesn't accept any parameters and returns a new Python dictionary. Let's use the previous example to serialize the User instance.
newPlainUser = user.to_dict()
print(newPlainUser['company']) # it's a dict now
print(newPlainUser['created']) # it's an ISO 8601 string now.
Copy/Clone
If you've already noticed, congratulations! Because our serialization methods safely copy data from/to dictionary, we can actually use them to create a deep clone/copy of any model. Check out the example below.
from models.user import User
from models.company import Company
if __name__ == "__main__":
user = User()
user.email = 'john@doe.com'
user.company = Company.from_dict({"name": "Cat Cafe, LLC."})
clone = User.from_dict(user.to_dict())
clone.company.name = 'Dog Cafe, LLC.' # we can just rename the company and leave the rest
print(user.company.name) # prints out 'Cat Cafe, LLC.'
print(clone.company.name) # prints out 'Dog Cafe, LLC.'
Date format
We use Python's standard datetime object for model generation. As for serialization, ISO 8601 format is expected. In other words, from_dict method expects all date/datetime fields to be in the ISO 8601 format and to_dict converts all datetime objects into the ISO 8601 format. For instance, January 5th, 2022 at 11:45 am would be represented as 2022-01-05T11:45:00.000Z