model_apps
and the importance for Edgy and Saffier¶
Today marks a great milestone achieved for Edgy and Saffier.
Edgy and Saffier, both ORMs with different purposes and use cases were known for for being framework agnostic but there was a known problem that was causing some headaches when migrations where the subject.
What was the headache?¶
When using Edgy/Saffier, declaring models is very simple and very straightforward. With their ease of use and friendly interface, it looks like assembling legos but with database tables.
Example with Edgy
import edgy
database = edgy.Database("sqlite:///db.sqlite")
registry = edgy.Registry(database=database)
class User(edgy.Model):
id: int = edgy.IntegerField(primary_key=True)
name: str = edgy.CharField(max_length=255)
email: str = edgy.CharField(max_length=100)
username: str = edgy.CharField(max_length=50)
class Meta:
registry = registry
Example with Saffier
import saffier
database = saffier.Database("sqlite:///db.sqlite")
registry = saffier.Registry(database=database)
class User(saffier.Model):
id = saffier.IntegerField(primary_key=True)
name = saffier.CharField(max_length=255)
email = saffier.CharField(max_length=100)
username = saffier.CharField(max_length=50)
class Meta:
registry = registry
Both extremly similar with the key difference that Edgy is 100% built on top of Pydantic and Saffier is not, making it more of generic ORM.
The headaches were when generating the migrations for both of them, both offer a Migrate
object
that facilitates the discovery of the models and automate the migrations but this would also happen
if the models were being imported somehow in the top level of a package of if explicitly declared.
Anyway, this was not a blocker as you could also change the migrations generated by both of them and import your models for generation but again, whatif we could make it simpler? Just in one place?
Enters model_apps
¶
From today, the release 0.10.0
of Edgy and 1.4.0
of Saffier offer the model_apps
parameter
that aims to solve this issue.
No more workarounds and no more hacks for migrations anymore.
Example¶
Let us assume we have an application with the following structure using Edgy (the same is for Saffier).
.
└── README.md
└── .gitignore
└── myproject
├── __init__.py
├── apps
│ ├── __init__.py
│ └── accounts
│ ├── __init__.py
│ ├── tests.py
│ ├── models.py
│ └── v1
│ ├── __init__.py
│ ├── schemas.py
│ ├── urls.py
│ └── views.py
├── configs
│ ├── __init__.py
│ ├── development
│ │ ├── __init__.py
│ │ └── settings.py
│ ├── settings.py
│ └── testing
│ ├── __init__.py
│ └── settings.py
├── main.py
├── serve.py
├── utils.py
├── tests
│ ├── __init__.py
│ └── test_app.py
└── urls.py
As you can see, it is quite structured but let us focus specifically on accounts/models.py
.
There is where your models for the accounts
application will be placed. Something like this:
from datetime import datetime
from my_project.utils import get_db_connection
import edgy
_, registry = get_db_connection()
class User(edgy.Model):
"""
Base model for a user
"""
first_name: str = edgy.CharField(max_length=150)
last_name: str = edgy.CharField(max_length=150)
username: str = edgy.CharField(max_length=150, unique=True)
email: str = edgy.EmailField(max_length=120, unique=True)
password: str = edgy.CharField(max_length=128)
last_login: datetime = edgy.DateTimeField(null=True)
is_active: bool = edgy.BooleanField(default=True)
is_staff: bool = edgy.BooleanField(default=False)
is_superuser: bool = edgy.BooleanField(default=False)
class Meta:
registry = registry
Now we want to tell the Migrate object to make sure it knows about this.
#!/usr/bin/env python
import os
import sys
from pathlib import Path
from my_project.utils import get_db_connection
from edgy import Migrate
from esmerald import Esmerald, Include
def build_path():
"""
Builds the path of the project and project root.
"""
Path(__file__).resolve().parent.parent
SITE_ROOT = os.path.dirname(os.path.realpath(__file__))
if SITE_ROOT not in sys.path:
sys.path.append(SITE_ROOT)
sys.path.append(os.path.join(SITE_ROOT, "apps"))
def get_application():
"""
This is optional. The function is only used for organisation purposes.
"""
build_path()
database, registry = get_db_connection()
app = Esmerald(
routes=[Include(namespace="my_project.urls")],
)
Migrate(
app=app,
registry=registry,
model_apps={"accounts": "accounts.models"},
)
return app
app = get_application()
As you can see the model_apps = {"accounts": "accounts.models"}
was added in a simple fashion.
Every time you add new model or any changes, it should behave as normal as before with the key difference that now Edgy has a way to know exactly where your models are specifically.
This solves the headaches of discovering the models within any application no matter what location you place them as
long as you specify in the model_apps
where to look at.