Defining Models in PormG
PormG models describe the structure of your database tables using Julia code, inspired by Django ORM but tailored for Julia's syntax and performance.
What is a Model?
A model is a Julia object that defines the fields (columns) and their types for a database table. Each model maps directly to a table in your PostgreSQL or SQLite database.
Creating a Model
Edit Your Models File
- By default, models are defined in
db/models.jl. - Each model is a Julia struct using PormG field types.
- By default, models are defined in
Example Model Definition
Driver = Models.Model(
id = Models.IDField(),
name = Models.CharField(max_length=100),
birthdate = Models.DateField(),
nationality = Models.CharField(max_length=50)
)- Example of module contruction in
db/models.jl
module models
import PormG.Models
import PormG.Models: RESTRICT, CASCADE, SET_NULL, SET_DEFAULT, DO_NOTHING
Status = Models.Model(
statusId = Models.IDField(),
status = Models.CharField()
)
Circuit = Models.Model( # You can create a model like a Django model for each table so that you can define a huge number of tables at once in just one file. Please capitalize the names of models.
circuitId = Models.IDField(), # the PormG automatically do a lowercase for the name of the field, so you can use a capital letter in the name of the field, Hoewver you need to use a lowercase in the query operations.
circuitRef = Models.CharField(),
name = Models.CharField(),
location = Models.CharField(),
country = Models.CharField(),
lat = Models.FloatField(),
lng = Models.FloatField(),
alt = Models.IntegerField(),
url = Models.CharField()
)
Race = Models.Model(
raceId = Models.IDField(),
year = Models.IntegerField(),
round = Models.IntegerField(),
circuitId = Models.ForeignKey(Circuit, pk_field="circuitId", on_delete="CASCADE"),
name = Models.CharField(),
date = Models.DateField(),
time = Models.TimeField(null=true),
url = Models.CharField(),
fp1_date = Models.DateField(null=true),
fp1_time = Models.TimeField(null=true),
fp2_date = Models.DateField(null=true),
fp2_time = Models.TimeField(null=true),
fp3_date = Models.DateField(null=true),
fp3_time = Models.TimeField(null=true),
quali_date = Models.DateField(null=true),
quali_time = Models.TimeField(null=true),
sprint_date = Models.DateField(null=true),
sprint_time = Models.TimeField(null=true),
)
Driver = Models.Model(
driverId = Models.IDField(),
driverRef = Models.CharField(),
number = Models.IntegerField(null=true),
code = Models.CharField(),
forename = Models.CharField(),
surname = Models.CharField(),
dob = Models.DateField(),
nationality = Models.CharField(),
url = Models.CharField()
)
Constructor = Models.Model(
constructorId = Models.IDField(),
constructorRef = Models.CharField(),
name = Models.CharField(),
nationality = Models.CharField(),
url = Models.CharField()
)
Result = Models.Model(
resultId = Models.IDField(),
raceId = Models.ForeignKey(Race, pk_field="raceId", on_delete="CASCADE"),
driverId = Models.ForeignKey(Driver, pk_field="driverId", on_delete="RESTRICT"),
constructorId = Models.ForeignKey(Constructor, pk_field="constructorId", on_delete="RESTRICT"),
number = Models.IntegerField(null=true),
grid = Models.IntegerField(),
position = Models.IntegerField(null=true),
positionText = Models.CharField(),
positionOrder = Models.IntegerField(),
points = Models.FloatField(),
laps = Models.IntegerField(),
time = Models.CharField(null=true),
milliseconds = Models.IntegerField(null=true),
fastestLap = Models.IntegerField(null=true),
rank = Models.IntegerField(null=true),
fastestLapTime = Models.TimeField(null=true),
fastestLapSpeed = Models.FloatField(null=true),
statusId = Models.ForeignKey(Status, pk_field="statusId", on_delete="CASCADE")
)
Just_a_test_deletion = Models.Model(
id = Models.IDField(),
name = Models.CharField(),
test_result = Models.ForeignKey(Result, pk_field="resultId", on_delete="CASCADE", null=true, related_name="test_deletion"),
test_result2 = Models.ForeignKey(Result, pk_field="resultId", on_delete="CASCADE", null=true, related_name="test_deletion2")
)
Models.set_models(@__MODULE__, @__DIR__) # That is important to set the models in the module, otherwise it will not work, that need stay at the end of the file
end- Each field uses a PormG field constructor (e.g.,
IDField,CharField,DateField). - You can use keyword arguments to customize field options (e.g.,
max_length,unique,null).
Naming Conventions and Considerations
Model Naming Rules
- Use snake_case with capitalized first letter:
User,Product,Order_item - Use singular nouns:
UsernotUsers,ProductnotProducts - Be descriptive and clear:
User_profile,Product_category,Order_history
Model Organization
- Keep models in
db/models.jlor similar organized structure - Group related models together in logical sections
- Use meaningful comments to explain complex relationships
Development Workflow
PormG supports two primary workflows for model creation:
1. Model-First (Recommended for New Projects)
- Define your models in a
models.jlfile. - Use
PormG.Migrations.makemigrations()to detect changes. - Use
PormG.Migrations.migrate()to apply them to your database.
2. DB-First (Legacy or Existing Databases)
If you already have a database, you can use the PormG.setup() utility to generate your model code automatically:
using PormG
# This will introspect the DB and create a basic models.jl for you
PormG.setup("path/to/my/db") Loading Models in Your Application
Using @import_models (Recommended)
The @import_models macro is the recommended way to load models in your application:
# In your main module (e.g., mypkg.jl):
module MyApp
using PormG
# Load models from external file with hot-reload support
PormG.@import_models "db/models.jl" my_models
import .my_models as M
# Now use M.Driver, M.Race, M.Result, etc.
# Models automatically update when you edit db/models.jl and save
endWhat @import_models Does
- Resolves the model file path relative to your source file
- Tracks the file with Revise (if available) for hot-reloading in interactive sessions
- Registers models with PormG so fields and metadata are indexed
- Injects
__init__()to re-register models after package precompilation - Enables hot-reloading: Edit your
models.jl, save, and model changes appear instantly in the REPL
Manual Registration (Inline Models)
If you define models directly in code instead of a separate file:
module my_models
import PormG.Models as M
Driver = M.Model("drivers",
driverId = M.IDField(),
forename = M.CharField()
)
# REQUIRED: Register the module so PormG can find it for queries and migrations
M.set_models(@__MODULE__, @__DIR__)
endLegacy: Direct set_models() Call
At the end of your db/models.jl file, you no longer need to call Models.set_models() manually if using @import_models. However, if loading the model module directly, include this line at the end:
Models.set_models(@__MODULE__, @__DIR__)Hot-Reloading Model Definitions
When using @import_models with Revise.jl, model changes are automatically detected and applied:
# Your REPL session (with Revise.jl loaded):
julia> using MyApp
julia> M.Driver.fields # Shows current fields: id, name
# Edit db/models.jl to add a field, save the file...
julia> M.Driver.fields # Automatically updated with new field!This enables rapid development and testing without restarting Julia. When you modify models:
- Add new fields
- Remove fields
- Change field types
- Adjust field parameters
All changes are automatically reloaded and available in the next REPL command.
Supported Field Types
PormG provides comprehensive field types for all common database scenarios:
- Primary Key Fields:
IDField,AutoField - Text Fields:
CharField,TextField,EmailField - Numeric Fields:
IntegerField,BigIntegerField,FloatField,DecimalField - Date/Time Fields:
DateField,DateTimeField,TimeField,DurationField - Other Types:
BooleanField,ImageField,BinaryField - Relationship Fields:
ForeignKey,OneToOneField
For detailed documentation on each field type, including parameters, examples, and best practices, see Field Types Reference.
Post-Precompilation Behavior
When your package is precompiled (e.g., after import MyPkg), the @import_models macro ensures models are automatically re-registered via injected __init__() functions. This means:
- Models are available in package code without manual registration
- Hot-reloading continues to work in interactive sessions
- No additional setup is required for REPL users
For more details, see the PormG Documentation or the example scripts in the test/integration/ folder.