API Reference
Overview
PormG provides a Django-inspired ORM for Julia with an async-first architecture. This page serves as a comprehensive reference for all exported functions, types, and the query builder API. For topic-specific guides, see the Reading and Writing sections.
Query Builder: Functor API
PormG uses a Django-style, object-oriented query builder. All database operations start from Model.objects and are chained using methods that either modify the query or execute it.
# The general pattern
results = M.Driver.objects
.filter("nationality" => "Brazilian")
.values("forename", "surname")
.order_by("surname")
.limit(10)
.list()Chainable Methods
These methods modify the query builder and return the handler for further chaining:
| Method | Description | Example |
|---|---|---|
.filter(key => value, ...) | Add WHERE conditions (AND). Multiple pairs are ANDed. | .filter("nationality" => "British") |
.values("field1", "field2", ...) | Select specific columns. Use "*" for all main-table columns. | .values("*", "driverId__surname") |
.order_by("field", "-field") | Sort results. Prefix with - for descending. | .order_by("-points", "surname") |
.limit(n) | Limit the number of returned rows. | .limit(10) |
.offset(n) | Skip the first n rows. | .offset(20) |
.db("key") | Route the query to a different connection pool. | .db("tenant_42") |
.on("path", key => value) | Add predicates to the ON clause of an existing join path. | .on("driverId", "nationality" => "British") |
See also: cjoin() for custom join definitions and with() for CTEs.
[!IMPORTANT] Queries that use
cjoin()must call.values(...)explicitly before execution. A bareSELECT *across joined tables causesDataFrames.jlto crash withArgumentError: Duplicate variable names. PormG throws a clear error if you forget. Use.values("*", "joined_model__field")to quickly select all main-table columns plus specific fields from the joined table.
Terminal Methods
These methods finalize the query and execute it against the database:
| Method | Return Type | Description |
|---|---|---|
.list() | Vector{Dict{Symbol, Any}} | Returns all matching rows as dictionaries. |
.all() | Vector{Dict} | Alias for .list(). |
query |> DataFrame | DataFrame | Pipe to DataFrame for tabular output. |
.count() | Int | Runs SELECT COUNT(*) and returns the count. |
.exists() | Bool | Returns true if at least one row matches. |
.first() | Dict or nothing | Returns the first matching record or nothing. |
.list_json() | String | Returns results as a JSON string. |
.create(key => value, ...) | Dict | Inserts a single record and returns it. |
.update(key => value, ...) | — | Updates all matching records. |
.delete() | — | Deletes all matching records. |
Example:
# Full query chain with DataFrame output
df = M.Result.objects
.filter("driverId__nationality" => "Brazilian", "positionOrder" => 1)
.values("driverId__forename", "driverId__surname", "raceId__year")
.order_by("-raceId__year") |> DataFrame
# Count and existence checks
n = M.Driver.objects.filter("nationality" => "British").count()
has_british = M.Driver.objects.filter("nationality" => "British").exists()Query Inspection & Debugging
show_query
An integrated switch available on all terminal methods to toggle between execution and inspection.
| Mode | Description |
|---|---|
:execute | Default. Executes the query and returns results. |
:sql | Returns the SQL string only. Minimal overhead for benchmarking. |
:dict | Returns full metadata dictionary (sql, parameters, dialect, model, operation, etc.). |
:params | Returns the parameters array only. |
:none | Returns nothing. Zero-overhead mode for benchmarking the builder itself. |
query = M.Driver.objects.filter("nationality" => "British")
# Get just the SQL string
sql = query.list(show_query=:sql)
# Benchmark the builder without execution overhead
@time query.list(show_query=:none)
# Get full metadata
meta = query.list(show_query=:dict)inspect_query
Dedicated API for comprehensive query inspection without executing. Features a heuristic intent detector that guesses the operation type (select, insert, update) based on the object state.
query = M.Driver.objects.filter("nationality" => "Brazilian").order_by("surname")
inspection = query |> inspect_query()
println(inspection[:sql]) # The generated SQL
println(inspection[:parameters]) # Bound parameters
println(inspection[:operation]) # Automatically detects :select
println(inspection[:dialect]) # :postgresql or :sqlite[!NOTE]
LIMITandOFFSETvalues are rendered as literal integers in the SQL string. They do not appear ininspection[:parameter_buckets]orinspection[:parameters]. This is by design.
Filter Operators
PormG uses __@ suffixes for lookup operators and field transforms:
Comparison Operators
| Operator | SQL Equivalent | Example |
|---|---|---|
field | = value | "nationality" => "British" |
field__@gt | > value | "points__@gt" => 10 |
field__@gte | >= value | "points__@gte" => 10 |
field__@lt | < value | "positionOrder__@lt" => 3 |
field__@lte | <= value | "positionOrder__@lte" => 10 |
field__@ne | <> value | "status__@ne" => "Retired" |
field__@in | IN (...) | "nationality__@in" => ["British", "French"] |
field__@nin | NOT IN (...) | "nationality__@nin" => ["British", "German"] |
field__@range | BETWEEN a AND b | "driverId__@range" => [1, 50] |
field__@isnull | IS NULL / IS NOT NULL | "dob__@isnull" => true |
field__@contains | LIKE '%val%' | "name__@contains" => "Monaco" |
field__@icontains | ILIKE '%val%' | "name__@icontains" => "monaco" |
Transform Functions
| Transform | Description | Example |
|---|---|---|
field__@year | Extract year from date | "dob__@year" => 1960 |
field__@month | Extract month from date | "dob__@month" => 3 |
field__@day | Extract day from date | "dob__@day" => 21 |
field__@quarter | Extract quarter (1-4) | "date__@quarter" => 1 |
field__@date | Extract date from datetime | "created_at__@date" => Date(2025, 1, 1) |
field__@round | Round numeric value | "points__@round" => 0 |
field__@floor | Floor numeric value | "points__@floor" => 0 |
field__@ceil | Ceiling numeric value | "points__@ceil" => 0 |
For the full list of operators and transforms, see Filters and Aggregates.
F-Expressions
F() enables database-side field references and arithmetic. Use it for field-to-field comparisons and computed expressions.
using PormG: F, Sum, Count
# Field-to-field comparison
M.Result.objects.filter(F("grid") == F("positionOrder"))
# Arithmetic in projections
M.Result.objects.values(
"driverId__surname",
"bonus" => F("points") * 0.1
)
# Aggregate ratios
M.Result.objects.values(
"driverId__surname",
"avg_pts" => Sum("points") / Count("resultId")
)
# Atomic update (no read-modify-write race)
M.Result.objects.filter("resultId" => 1).update("points" => F("points") + 10)See Field Expressions for the full reference.
Q Objects: Complex Boolean Logic
Q() and Qor() enable complex boolean predicates with AND/OR logic:
using PormG: Q, Qor
# AND logic (Q contains AND by default)
M.Driver.objects.filter(Q("nationality" => "Brazilian", "code" => "SEN"))
# OR logic
M.Driver.objects.filter(Qor("nationality" => "Brazilian", "nationality" => "French"))
# Nested AND/OR
M.Driver.objects.filter(
Q("nationality" => "Brazilian", Qor("forename" => "Ayrton", "forename" => "Nelson"))
)See Q Objects for the full reference.
Aggregate Functions
All aggregate functions can be used in .values() for grouping or combined with F-expressions:
| Function | SQL | Example |
|---|---|---|
Count("field") | COUNT(field) | "total" => Count("resultId") |
Sum("field") | SUM(field) | "total_pts" => Sum("points") |
Avg("field") | AVG(field) | "avg_pts" => Avg("points") |
Max("field") | MAX(field) | "best" => Max("points") |
Min("field") | MIN(field) | "worst" => Min("points") |
When aggregate values appear in values(), PormG automatically groups by the non-aggregated columns. Aggregate-based filters are promoted to HAVING.
# Wins per constructor with HAVING filter
df = M.Result.objects.values(
"constructorId__name",
"wins" => Count("resultId")
).filter(
"positionOrder" => 1,
"wins__@gt" => 50
).order_by("-wins") |> DataFrameSQL Functions
PormG exports a comprehensive set of SQL functions:
String Functions
| Function | Description | Example |
|---|---|---|
Lower("field") | Convert to lowercase | "name_lower" => Lower("surname") |
Upper("field") | Convert to uppercase | "name_upper" => Upper("surname") |
Length("field") | String length | "name_len" => Length("surname") |
Concat(args...) | Concatenate values | "full" => Concat("forename", Value(" "), "surname") |
Trim("field") | Trim whitespace | "clean" => Trim("name") |
LTrim("field") | Left trim | "clean" => LTrim("name") |
RTrim("field") | Right trim | "clean" => RTrim("name") |
Replace("field", old, new) | Replace substring | "fixed" => Replace("name", "-", " ") |
Numeric Functions
| Function | Description |
|---|---|
Abs("field") | Absolute value |
Round("field", precision) | Round to precision |
Floor("field") | Floor |
Ceil("field") | Ceiling |
Sqrt("field") | Square root |
Exp("field") | Exponential |
Ln("field") | Natural logarithm |
Power("field", n) | Raise to power |
Mod("field", n) | Modulo |
Conditional & Utility Functions
| Function | Description | Example |
|---|---|---|
Value(x) | Literal value in SQL | Value("hello") |
Coalesce(args...) | First non-null value | Coalesce("nickname", "forename") |
NullIf("field", value) | Returns NULL if equal | NullIf("code", "") |
Greatest(args...) | Maximum of values | Greatest("points", Value(0)) |
Least(args...) | Minimum of values | Least("points", Value(100)) |
Cast("field", type) | Type casting | Cast("points", "INTEGER") |
Extract("part", "field") | Extract date/time part | Extract("year", "dob") |
To_char("field", fmt) | Format to string | To_char("dob", "YYYY-MM") |
Case Expressions
Case(
("positionOrder" => 1) => Value("Winner"),
("positionOrder__@lte" => 3) => Value("Podium"),
default=Value("Other")
)See Functions and Dates for more details.
Custom Joins
cjoin()
Defines custom join conditions at query time. Useful for legacy databases, non-FK joins, and multi-tenant systems.
using PormG: cjoin, Q, Qor
query = M.Result.objects
cjoin(query, "driverId" => "Driver",
filters=[Q("nationality" => "Brazilian", Qor("forename" => "Ayrton", "forename" => "Nelson"))],
join_type="INNER"
)
query.values("driverId__forename", "driverId__surname", "points")
df = query |> DataFrameParameters:
| Argument | Type | Description |
|---|---|---|
query | handler | The query object to add the join to. |
main_join | Pair{String,String} | "field" => "TargetModel" — the join path. |
filters | Vector | ON-clause predicates. Supports Pair, Q(), Qor(), F expressions. |
join_type | String | "LEFT" (default), "INNER", "RIGHT", or "FULL". |
field | PormGField | Optional custom field definition for non-FK joins. |
warn | Bool | Suppress auto-discovery warnings (default: true). |
See Custom Joins for the full documentation.
on()
Adds ON-clause predicates to existing join paths (including reverse joins) without redefining them:
query = M.Result.objects
query.on("driverId", "nationality" => "Brazilian", "code" => "SEN")
query.values("resultId", "driverId__surname", "points")Bulk Operations
bulk_insert
Inserts multiple records in a single operation. Returns the inserted rows.
df = DataFrame([
Dict("forename" => "Ayrton", "surname" => "Senna", "nationality" => "Brazilian"),
Dict("forename" => "Alain", "surname" => "Prost", "nationality" => "French"),
])
result = bulk_insert(M.Driver, df)bulk_update
Updates multiple records in a single operation.
bulk_update(M.Driver.objects, df_with_changes)bulk_copy
⭐ PostgreSQL Only. Uses PostgreSQL's native COPY FROM STDIN protocol for ultra-fast bulk loading.
bulk_copy(M.Driver.objects, df)| Function | Best For | Speed | Protocol |
|---|---|---|---|
create() | Single rows | Standard | SQL INSERT |
bulk_insert() | Medium datasets (< 10k rows) | Fast | Multi-row INSERT |
bulk_copy() ⭐ | Massive datasets | Ultra-Fast | Postgres COPY |
bulk_update() | Modifying many rows | Fast | Multi-row UPDATE |
Transactions
run_in_transaction
Executes a block inside a database transaction with automatic commit/rollback:
PormG.run_in_transaction("db") do
M.Result.objects.create("raceId" => 1, "driverId" => 1, "points" => 25)
M.Driver.objects.filter("driverId" => 1).update("code" => "WIN")
# If any exception is raised, both operations are rolled back
endKey features:
- Async context propagation — spawned
@asynctasks inherit the transaction - Connection sharing — all queries in the block use the same connection
- Automatic rollback on error
See Transactions for the full reference including savepoints and multithreaded patterns.
Async Execution
fetch_async
Execute a query asynchronously, returning a FetchTask:
task = fetch_async(M.Driver.objects.filter("nationality" => "Brazilian"))
# ... do other work ...
result = await_result(task)Configuration API
Configuration.load(path; env=nothing)
Loads a database configuration folder. Use env to explicitly set the environment instead of relying on ENV["PORMG_ENV"].
PormG.Configuration.load("db"; env="prod")Configuration.load_many(paths; env=nothing)
Bootstraps multiple database folders in one call:
PormG.Configuration.load_many(["db", "db_analytics"]; env="prod")Configuration.is_loaded(path_or_key)
Returns true if PormG has registered settings for the given folder/key. Does not open connections.
Configuration.ping(path_or_key)
Tests actual database reachability. Returns Bool.
Configuration.status(path_or_key)
Returns a rich status payload:
s = PormG.Configuration.status("db")
# (key="db", loaded=true, reachable=true, adapter="PostgreSQL", app_env="prod")For the full configuration guide, see Configuration.
Advisory Locks
with_advisory_lock
Acquires a PostgreSQL advisory lock for distributed coordination:
PormG.with_advisory_lock("db", "migration_lock"; wait=true, timeout_ms=10000) do
# Critical section — only one process at a time
PormG.Migrations.migrate("db")
endStrategies:
:poll(default) — Client-side polling with interval:block— Server-side blocking viapg_advisory_lock
SQLite: No-op (executes the block without locking).
See Advisory Locks for the full reference.
Password Utilities
make_password(raw_password)
Hashes a password using the default algorithm (PBKDF2-SHA256, 720,000 iterations):
hash = make_password("Champion_1988!")
# "pbkdf2_sha256$720000$salt$hash..."check_password(raw_password, encoded_hash)
Verifies a password against its stored hash with constant-time comparison:
if check_password("Champion_1988!", stored_hash)
println("Welcome!")
endpassword_needs_upgrade(encoded_hash)
Returns true if the hash should be re-calculated with stronger settings:
if password_needs_upgrade(stored_hash)
new_hash = make_password(raw_password)
endvalidate_password(password; kwargs...)
Validates password complexity. Returns a ValidationResult with .valid and .errors.
result = validate_password("weak", min_length=10)
if !result.valid
println(result.errors)
endSee Passwords for algorithms, custom encoders, i18n, and Django/Spring compatibility.
Terminal Dashboard
tui(db_path; models_module=nothing, fps=30)
Launches an interactive terminal dashboard for migration review and query inspection. Requires Tachikoma.jl.
using Tachikoma
PormG.tui("db"; models_module=M)See Migrations: Terminal Dashboard for details.
Abstract Types
PormG's type hierarchy provides the foundation for the query builder and model system:
| Type | Description |
|---|---|
PormGAbstractType | Base abstract type for all PormG types. |
SQLConn | Represents a database connection (subtypes: PormGPostgres, PormGSQLite). |
SQLObject | Base for objects that can be stored in the database. |
SQLObjectHandler | Handles operations on SQL objects (the query builder). |
SQLTableAlias | Manages table aliases in SQL queries. |
SQLInstruction | Represents an instruction to build a SQL query. |
SQLType | Base for SQL-related expression types. |
SQLTypeField | Represents a field expression in queries. |
SQLTypeQ | Q-expression type (AND logic). |
SQLTypeQor | Qor-expression type (OR logic). |
SQLTypeF | F-expression type (field references). |
SQLTypeFunction | SQL function type. |
SQLTypeCTE | Common Table Expression type. |
PormGModel | Base for model types. |
PormGField | Base for field type definitions. |
Exported Symbols
The following symbols are exported by PormG and available after using PormG:
Query Builder
object, Q, Qor, F, show_query, inspect_query
Aggregate Functions
Sum, Avg, Count, Max, Min
SQL Functions
Case, Cast, Concat, Extract, To_char, Value, Coalesce, Greatest, Least, Lower, Upper, Length, Abs, Round, NullIf, Replace, Trim, LTrim, RTrim, Floor, Ceil, Sqrt, Exp, Ln, Power, Mod
Bulk Operations
bulk_insert, bulk_update, bulk_copy
Async API
fetch_async, await_result, FetchTask
Transactions
run_in_transaction, with_tx_context, in_transaction_context
Locking
with_advisory_lock
Passwords
make_password, check_password, password_needs_upgrade, validate_password, ValidationResult, PasswordValidator
Utilities
setup, tui, @import_models, @models_module
Auto-Generated API Docs
The following section contains auto-generated documentation from docstrings in the source code:
PormG.setup — Function
setup(path::String = DB_PATH)Interactively setup the connection.yml file for PormG. This will prompt for database adapter, name, and connection details.
PormG.QueryBuilder.Abs — Method
Abs(column)Returns the absolute value of a number.
PormG.QueryBuilder.Cast — Method
Cast(expression, type)Casts a column or expression to a specific SQL type.
PormG.QueryBuilder.Ceil — Method
Ceil(column)Returns the smallest integer greater than or equal to a number.
PormG.QueryBuilder.Coalesce — Method
Coalesce(args...; output_field=nothing)Returns the first non-null value in the list of arguments.
PormG.QueryBuilder.Concat — Method
Concat(expressions; output_field=nothing)Concatenates multiple strings or columns.
PormG.QueryBuilder.Count — Method
Count(x; distinct::Bool = false)
Creates an aggregate COUNT function object for use in query building.
Arguments
x: The column or expression to count.distinct::Bool = false: Iftrue, counts only distinct values ofx.
Examples
# Count just when other_model_id is distinct
query = MyModels.model_test |> object;
query.filter("id__@gte" => 1)
query.values("id", "count" => Count("other_model_id", distinct=true))
df = query |> list |> DataFramePormG.QueryBuilder.Exp — Method
Exp(column)Returns the exponential value (e^x) of a number.
PormG.QueryBuilder.Extract — Method
Extract(column, part)Extracts a component (YEAR, MONTH, DAY, etc.) from a date/time column.
PormG.QueryBuilder.Floor — Method
Floor(column)Returns the largest integer less than or equal to a number.
PormG.QueryBuilder.Greatest — Method
Greatest(args...; output_field=nothing)Returns the greatest value in the list of arguments.
PormG.QueryBuilder.LTrim — Method
LTrim(column)Removes leading whitespace from a string.
PormG.QueryBuilder.Least — Method
Least(args...; output_field=nothing)Returns the least value in the list of arguments.
PormG.QueryBuilder.Length — Method
Length(column)Returns the length of a string.
PormG.QueryBuilder.Ln — Method
Ln(column)Returns the natural logarithm of a number.
PormG.QueryBuilder.Lower — Method
Lower(column)Converts a string to lowercase.
PormG.QueryBuilder.Mod — Method
Mod(dividend, divisor)Returns the remainder (modulo) of a division.
PormG.QueryBuilder.NullIf — Method
NullIf(field1, field2)Returns NULL if field1 equals field2, otherwise returns field1.
PormG.QueryBuilder.Power — Method
Power(base, exponent)Returns base raised to the power of exponent.
PormG.QueryBuilder.Q — Method
Q(x...)
Create a QObject with the given filters. Ex.: julia a = object("tb_user") a.filter(Q("name" => "John", Qor("age" => 18, "age" => 19)))
Arguments:
x: A list of key-value pairs or Qor(x...) or Q(x...) objects.
PormG.QueryBuilder.Qor — Method
Qor(x...)
Create a QorObject from the given arguments. The QorObject represents a disjunction of SQLTypeQ or SQLTypeQor objects.
Ex.: julia a = object("tb_user") a.filter(Qor("name" => "John", Q("age__gte" => 18, "age__lte" => 19)))
Arguments
x...: A variable number of arguments. Each argument can be either aSQLTypeQorSQLTypeQorobject, or aPairobject.
PormG.QueryBuilder.RTrim — Method
RTrim(column)Removes trailing whitespace from a string.
PormG.QueryBuilder.Replace — Method
Replace(column, find, replace)Replaces all occurrences of find with replace in the string.
PormG.QueryBuilder.Round — Function
Round(column, precision=0)Rounds a number to the specified precision.
PormG.QueryBuilder.Sqrt — Method
Sqrt(column)Returns the square root of a number.
PormG.QueryBuilder.Sum — Method
Sum(column; distinct=false)Computes the sum of all values in the column.
PormG.QueryBuilder.Trim — Method
Trim(column)Removes leading and trailing whitespace from a string.
PormG.QueryBuilder.Upper — Method
Upper(column)Converts a string to uppercase.
PormG.QueryBuilder.Value — Method
Value(x)Wraps a literal value (String, Number, or Nothing) for use in SQL queries.
PormG.QueryBuilder.With — Function
Add a Common Table Expression (CTE) to the query object.
CTEs can be joined like regular tables using their field names.
Arguments
q::SQLObject: The SQL object to add the CTE to (available asquery.object)name::String: The name of the CTE (will be used as table name in JOINs and Lookups)query::SQLObjectHandler: The subquery that defines the CTE
Keyword Arguments
join_field::Pair{String, String}: The relationship for joining (e.g.,"local_field" => "cte_field").join_type::String: The SQL join type ("LEFT","INNER", etc). Default is"LEFT".
Returns
- The modified
SQLObjectwith the CTE added.
Examples
import PormG.models as M
# Example 1: Basic CTE with JOIN (Finding total results per driver)
drivers_stats = M.Result.objects
drivers_stats.values("driverid", "total_races" => Count("resultid"))
main_query = M.Result.objects
With(main_query.object, "stats", drivers_stats, join_field="driverid" => "driverid")
# Filter and select using CTE fields with "cte_name__" prefix
main_query.filter("resultid__@lte" => 100)
main_query.values("resultid", "driverid", "stats__total_races")
df = main_query |> DataFrame# Example 2: CTE with INNER JOIN and Multiple Aggregations
high_scorers = M.Result.objects
high_scorers.filter("points__@gte" => 10)
high_scorers.values("driverid", "max_points" => Sum("points"))
query = M.Driver.objects
With(query.object, "hs", high_scorers, join_field="driverid" => "driverid", join_type="INNER")
query.values("driverid", "surname", "hs_points" => "hs__max_points")
query.filter("driverid__@lte" => 50)
df = query |> DataFramePormG.QueryBuilder.With — Method
Pair-accepting overload for With, enabling the functor-style API:
# Django-idiomatic chain:
races_91 = M.Race.objects.filter("year" => 1991).values("raceid")
q = M.Result.objects
.with("r91" => races_91, join_field="raceid" => "raceid")
.filter("positionorder" => 1)The Pair first argument maps the CTE name to its sub-query handler. All keyword arguments are forwarded to the underlying With implementation.
PormG.QueryBuilder._apply_like_wildcards — Method
_apply_like_wildcards(value::Any, operator::String)::AnyApply appropriate LIKE wildcards based on the operator:
- contains/icontains: %value%
- startswith: value%
- endswith: %value
- Other operators: no wildcards
PormG.QueryBuilder._cache_join — Method
_cache_join(field::String, instruct::SQLInstruction)Checks if a join path field is already in the cache. If not, and it's a join path (contains __), it attempts to build the join and cache it. Returns true if the field is in cache (either before or after the attempt), false otherwise.
PormG.QueryBuilder._determine_join_type — Method
build a row to join
PormG.QueryBuilder._get_pair_to_oper — Method
getpairtooper(x::Pair)
Converts a Pair object to an OperObject. If the Pair's key is a string, it checks if it contains an operator suffix (e.g. "@gte", "@lte") and returns an OperObject with the corresponding operator. If the key does not contain an operator suffix, it returns an OperObject with the "=" operator. If the key is not a string, it throws an error.
Arguments
x::Pair: A Pair object to be converted to an OperObject.
Returns
OperObject: An OperObject with the corresponding operator and values.
PormG.QueryBuilder._infer_case_output_type — Method
Infer the output PormGField type for a CASE/WHEN expression by inspecting the then values from WHEN branches and the default/else value.
Returns IntegerField if all values are integers, FloatField if any are floats, or CharField as a safe fallback for strings or mixed types.
PormG.QueryBuilder._preset_cte_fields — Method
Set the field definitions from a CTE query to create temporary PormGField objects. This allows the CTE to be treated like a table with queryable fields for JOINs.
PormG.QueryBuilder._solve_field — Method
This function checks if the given field is a valid field in the provided model. If the field is valid, it returns the field name, potentially modified based on certain conditions.
PormG.QueryBuilder._validation_error — Method
validate_field_data(model::PormGModel, field::String, value::Any, operation::String; allow_primary_key::Bool = true)Validates that a value is compatible with the model field definition before SQL generation. Checks:
- Field existence in model.
- Primary key modification protection (disabled if allowprimarykey is false).
- Max length for CharFields.
- Max digits for Decimal/Numeric fields.
Returns true if valid, throws an ErrorException otherwise. SQL expressions (SQLTypeF, SQLTypeFunction) skip data validation as they are evaluated by the DB.
PormG.QueryBuilder.build_cte_clause — Method
Build CTE (WITH clause) SQL string from the CTEs defined in the query object.
Arguments
ctes::Dict{String, Dict{String, Union{SQLObjectHandler, PormGField}}}: Dict of CTE name => fields dictconnection: Database connection for quoting identifiersparameters: Parameterized query object to collect all parameters
Returns
- String containing the WITH clause SQL, or empty string if no CTEs
PormG.QueryBuilder.bulk_copy — Method
bulk_copy(objct::SQLObjectHandler, df_o::DataFrames.DataFrame; kwargs...)Performs a high-speed bulk insert operation using PostgreSQL's COPY protocol. This is significantly faster than bulk_insert for large datasets.
Arguments
objct::SQLObjectHandler: The database handler object (e.g.,M.Model).df_o::DataFrames.DataFrame: The DataFrame containing the data to be inserted.columns: (Optional) Specifies which columns to insert. Can be aString, aPair{String, String}, or aVectorof these.copy::Bool = true: Iftrue, creates a copy of the DataFrame before processing.show_query::Bool = false: Iftrue, prints theCOPYcommand (note: data stream is not printed).
Example
bulk_copy(M.Driver, df)PormG.QueryBuilder.bulk_insert — Method
Inserts multiple rows into the database in bulk from a DataFrame.
Arguments
objct::SQLObjectHandler: The SQL object handler to use for the operation.df_o::DataFrames.DataFrame: The DataFrame containing the data to be inserted.columns: Optional. Specifies the columns to insert and their mappings. Can benothing, aString, aPair{String, String}, or aVectorof these. Ifnothing, all columns from the DataFrame are used.chunk_size::Integer: Optional. The number of rows to insert in each batch (default: 1000).show_query::Bool: Optional. If true, prints the generated SQL query (default: false).copy::Bool: Optional. If true, creates a copy of the DataFrame before processing (default: false).
Examples
```julia include("models.jl") import models as mdl
Basic usage
query = mdl.User |> object df = DataFrame(name=["Alice", "Bob"], age=[30, 25]) bulk_insert(query, df)
With column mapping and excluding unwanted variables
query = mdl.Boook |> object df = DataFrame(title=["Book A", "Book B"], authorname=["Alice", "Bob"], year=[2020, 2021], ignoreme=["x", "y"])
Map DataFrame column "author_name" to model field "author"
Exclude "ignore_me" by not including it in the columns argument
bulkinsert(query, df, columns=["title", "year", "authorname" => "author"])
the df will be modified to only include the columns "title", "year", and "author_name" (renamed to "author").
If you want to copy the DataFrame before processing, set copy=true:
bulkinsert(query, df, columns=["title", "year", "authorname" => "author"], copy=true)
```
PormG.QueryBuilder.bulk_update — Method
Performs a bulk update operation on a database table using the provided DataFrame and a query object.
Arguments
objct::SQLObjectHandler: The database handler object.df::DataFrames.DataFrame: The DataFrame containing the data to be used for the update.columns: (Optional) Specifies which columns to update. Can be aString, aPair{String, String}, or aVectorof these. Ifnothing, no columns are specified.filters: (Optional) Specifies the filters to apply for the update. Can be aString, aPair{String, T}whereTisString,Integer,Bool,Date, orDateTime, or aVectorof these. Ifnothing, no filters are applied.show_query::Bool: (Optional) Iftrue, prints the generated SQL query. Defaults tofalse.chunk_size::Integer: (Optional) Number of rows to process per chunk. Defaults to1000.copy::Bool: (Optional) Iftrue, creates a copy of the DataFrame before processing. Defaults totrue. Set tofalseto modify the original DataFrame and improve performance, but this may lead to unintended side effects when the operation is performed in asynchronous contexts.
Example
# Update the columns of the DataFrame df if df contains the primary key of the table
bulk_update(objct, df)
# Update the name and dof columns for the security_id in the DataFrame df
bulk_update(objct, df, columns=["security_id", "name", "dof"], filters=["security_id"])PormG.QueryBuilder.cjoin — Function
Add a custom join to the query object that does not have to follow the model's foreign key relationships.
Arguments
q::SQLObject: The SQL object to add the custom join tomain_join::Pair{String, String}: A pair where the first element is the field in the main model to join on, and the second element is the related model name to join withfilters::AbstractVector: (Optional) An array of filters (Pair, Q, Qor, OP, F) to apply to the join conditionjoin_type::Union{String, Nothing}: (Optional) The type of join
Examples
query = M.New_join_position |> object;
cjoin(query, "result" => "Result");
query.values("result__statusid__status", "description", "result");
@info query |> show_query
┌ Info: SELECT
│ "Tb_2"."status" as result__statusid__status,
│ "Tb"."description" as description,
│ "Tb"."result" as result
│ FROM "new_join_position" as "Tb"
│ LEFT JOIN "result" AS "Tb_1" ON "Tb"."result" = "Tb_1"."resultid"
└ LEFT JOIN "status" AS "Tb_2" ON "Tb_1"."statusid" = "Tb_2"."statusid"PormG.QueryBuilder.delete — Method
Delete objects from the database with proper handling of foreign key relationships and cascading operations.
Arguments
objct::SQLObjectHandler: The SQL object handler containing the query and model informationshow_query::Bool=false: Iftrue, displays the generated SQL queries instead of executing themallow_delete_all::Bool=false: Iftrue, allows deletion without WHERE clause filters (dangerous operation)
Returns
Tuple{Integer, Dict{String, Integer}}: A tuple containing:- Total number of deleted objects
- Dictionary mapping model names to their respective deletion counts
Behavior
- Validates that the connection allows data modification operations
- Requires WHERE clause filters unless
allow_delete_allis explicitly set totrue - Handles foreign key relationships by building a deletion dependency graph
- Processes SETNULL, SETDEFAULT, and cascading delete operations appropriately
- Executes all operations within a database transaction for data integrity
Examples
# Delete objects from a model with a specific filter
query = M.Status |> object
query.filter("status" => "Engine")
total, dict = delete(query)
# Show the SQL query without executing it
query = M.Just_a_test_deletion |> object
query.filter("test_result__constructorid__name" => "Williams")
total, dict = delete(query, show_query = :sql)
# Delete related tables (cascading delete)
query = M.Result |> object
query.filter("resultid" => 1)
total, dict = delete(query)
# Delete all objects from a model (use with caution)
query = M.Just_a_test_deletion |> object
total, dict = delete(query; allow_delete_all = true)
PormG.QueryBuilder.escape_like_pattern — Method
Escape LIKE patterns to prevent wildcard injection
PormG.QueryBuilder.get_filter_query — Method
getfilterquery(object::SQLObject, instruc::SQLInstruction)
Iterates over the filter of the object and generates the WHERE query for the given SQLInstruction object.
ALERT
- This internal function is called by the
buildfunction.
Arguments
object::SQLObject: The object containing the filter to be selected.instruc::SQLInstruction: The SQLInstruction object to which the WHERE query will be added.
PormG.QueryBuilder.get_final_parameters — Method
get_final_parameters(p::AbstractPormGParam) -> Vector{Any}Return all collected parameter values in the order expected by the final SQL string.
- PostgreSQL: returns the single linear vector (order already matches
$Nnumbering). - SQLite: concatenates buckets in standard SQL clause order:
cte → select → join → where → havingso that each positional?aligns with its value.
PormG.QueryBuilder.get_select_query — Method
getselectquery(object::SQLObject, instruc::SQLInstruction)
Iterates over the values of the object and generates the SELECT query for the given SQLInstruction object.
ALERT
- This internal function is called by the
buildfunction.
Arguments
object::SQLObject: The object containing the values to be selected.instruc::SQLInstruction: The SQLInstruction object to which the SELECT query will be added.
PormG.QueryBuilder.get_settings — Method
get_settings(obj::Union{SQLObject, SQLObjectHandler}; connection::Union{Nothing, PormGPostgres, PormGSQLite} = nothing)Resolves the database settings for a query. Returns a tuple of (settings::SQLConn, connection, conn_key::String).
If a connection is provided, it is returned as is. Otherwise, it returns the default connection from the resolved settings.
PormG.QueryBuilder.inspect_query — Method
inspect_query(q::SQLObjectHandler) -> DictComprehensive query inspection API that provides full metadata about a query without executing it. Returns a rich dictionary with SQL, parameters, dialect information, and structural metadata.
This is the explicit API for query inspection - use this when you want to examine a query's structure and generated SQL without ambiguity.
Arguments
q::SQLObjectHandler: The query object to inspectoperation::Union{Nothing, Symbol} = nothing: Optional operation override (:select, :insert, :update, :delete). If not provided, the operation is detected automatically based on the query structure.
Returns
Dict: A dictionary containing::sql_text(String): The generated SQL query:parameters(Vector): The parameterized values in bucket order:dialect(Symbol): The database dialect (:postgresqlor:sqlite):model(String): The model/table name:operation(Symbol): The query operation type (:select,:insert,:update,:delete):bucketing(Symbol): The parameter bucketing strategy (:numberedfor PostgreSQL,:positionalfor SQLite):parameter_count(Int): Number of parameters:parameter_buckets(Dict): Breakdown of parameters by bucket (for positional strategies)
Example
q = M.Driver.objects
q.filter("nationality" => "British")
q.order_by("surname")
inspection = q |> inspect_query()
# Dict with:
# :sql_text => "SELECT ... WHERE drivers.nationality = $1 ORDER BY ..."
# :parameters => ["British"]
# :dialect => :postgresql
# :model => "drivers"
# :operation => :select
# :bucketing => :numberedPormG.QueryBuilder.list — Method
Fetches a list of records from the database and returns them as an array of dictionaries.
This function executes the query and converts each row to a dictionary with column names as keys.
Arguments
objct::SQLObjectHandler: The SQL object handler containing the query
Returns
Vector{Dict{Symbol, Any}}: Array of dictionaries, where each dictionary represents a row
Example
query = M.Result |> object
query.filter("raceid__year" => 2020)
query.values("driverid__forename", "constructorid__name", "laps")
records = query |> list # Returns array of dictionaries
# Example output: [Dict(:driverid__forename => "Lewis", :constructorid__name => "Mercedes", :laps => 58), ...]PormG.QueryBuilder.list_json — Method
Fetches a list of records from the database and returns them as a JSON string.
This function executes the query, converts each row to a dictionary, and serializes the result as JSON.
Arguments
objct::SQLObjectHandler: The SQL object handler containing the query
Returns
String: JSON string representation of the query results
Example
query = M.Result |> object
query.filter("raceid__year" => 2020)
query.values("driverid__forename", "constructorid__name", "laps")
json_data = query |> list_json # Returns JSON string
# Example output: "[{"driverid__forename":"Lewis","constructorid__name":"Mercedes","laps":58}]"PormG.QueryBuilder.object — Method
Wraps a PormGModel into an ObjectHandler on which you can call:
- .filter(...) to add WHERE clauses
- .values(...) to choose/annotate columns
- .order_by(...) to sort
- .distinct() to add DISTINCT clause
- .create(...) for single-row DML
- .update(...) for single-row DML
- .limit(...), .offset(...), .page(...) for pagination
- plus bulk_insert, bulk_update, do_count, do_exists, list, etc.Arguments
model::PormGModel: The model to be wrapped and handled.
Example
using PormG, DataFrames
# assume models loaded as `M`
query = M.User.objects
# 1) Filtering & selecting
query.filter("is_active" => true)
query.values("id", "username", "email")
df = query |> DataFrame
# 2) Counting
active_users = query.count()
# 3) Inserting a single row
new = M.Status.objects.create("statusid" => 42, "status" => "Foo")
# returns a Dict of the inserted row
# 4) Updating a single row
M.Status.objects.filter("statusid" => 42).update("status" => "Bar")
# 5) Ordering & aggregation
query = M.Result.objects.filter("raceid__year" => 2020)
query.values(
"driverid__forename",
"constructorid__name",
"laps" => Count("laps")
).order_by("-laps")
df2 = query |> DataFrame
# 6) Existence check
exists = M.User.objects.filter("id" => 1).exists()
PormG.QueryBuilder.page — Method
Set pagination parameters for a SQL query object.
Arguments
object::SQLObjectHandler: The SQL object handler to modifylimit::Integer: Maximum number of records to return (default: 10)offset::Integer: Number of records to skip from the beginning (default: 0)
Examples
query.page(20, 10) |> DataFrame query.page(20) |> DataFrame
The function form page(query, 20, 10) is still supported, but the fluent query.page(...) style is the preferred public API.
PormG.QueryBuilder.query_list — Method
Fetches a list of records from the database for the given SQLObjectHandler.
Returns
- The result of the database query as returned by
fetch.
Example
query = M.Result |> object
query.filter("raceid__year" => 2020)
query.values("driverid__forename", "constructorid__name", "laps" => Count("laps"))
query.order_by("-laps")
df = query |> list |> DataFramePormG.QueryBuilder.quote_identifier — Method
Quote SQL identifiers based on database type
PormG.QueryBuilder.safe_field_identifier — Method
Validate field name against model and return quoted identifier
PormG.QueryBuilder.safe_table_identifier — Method
Validate and quote table name
PormG.QueryBuilder.sanitize_identifier — Method
Sanitize SQL identifiers (table names, column names) to prevent injection. Only allows alphanumeric characters, underscores, and validates against model schema.
PormG.QueryBuilder.set_context! — Method
set_context!(params::AbstractPormGParam, context::Symbol)Switch the active parameter bucket for positional-parameter backends (SQLite). Valid contexts: :cte, :select, :update, :join, :where, :having.
For numbered-parameter backends (PostgreSQL) this is a no-op.
PormG.QueryBuilder.up_filter! — Method
up_filter!(q::SQLObject, filter) Add filters to the SQLObject query.
Arguments
q::SQLObject: The SQL object to add filters to.filter: A collection of filters to add. Each filter can be aPair,SQLTypeQ,SQLTypeQor,SQLTypeOper, orSQLTypeF.
Returns
- The modified SQLObject with the new filters added.
DataFrames.DataFrame — Method
Creates a DataFrame directly from a SQLObjectHandler query.
This extends the DataFrame constructor to work directly with PormG query objects,
Arguments
objct::SQLObjectHandler: The SQL object handler containing the query
Returns
DataFrames.DataFrame: The query results as a DataFrame
Example
query = M.Result |> object
query.filter("raceid__year" => 2020)
query.values("driverid__forename", "constructorid__name", "laps")
df = query |> DataFrame # Direct conversion to DataFramePormG.QueryBuilder.CTEDict — Type
CTE configuration dictionary.
PormG.QueryBuilder.ChainCaller — Method
filter(args...)Apply filters to the query. Chainable method that returns the query object.
Usage: query.filter("field" => value)
See Read documentation for detailed filter syntax and examples.
PormG.QueryBuilder.FExpression — Type
F object for direct database field references and operations (similar to Django F expressions).
Allows you to reference database fields directly in operations without pulling data into Julia.
Examples
# Update a field with another field's value
query = MyModel |> object
query.filter("id" => 1)
query.update("field1" => F("field2"))
# Increment a field by a constant
query.update("counter" => F("counter") + 1)
# Update with arithmetic operations between fields
query.update("total" => F("price") * F("quantity"))
# Use in filters to compare fields
query.filter(F("start_date") <= F("end_date"))
# Use in annotations/values
query.values("price", "discounted_price" => F("price") * 0.9)PormG.QueryBuilder.JoinDict — Type
Join metadata dictionary.
PormG.QueryBuilder.OperObject — Type
Mutable struct representing an SQL operator object for using in the filter and annotate. That is a internal function, please do not use it.
Fields
operator::String: the operator used in the SQL query.values::Union{String, Integer, Bool}: the value(s) to be used with the operator.column::Union{String, SQLTypeFunction}: the column to be used with the operator.
PormG.Models.AutoField — Method
AutoField(; kwargs...)A field type for auto-incrementing integer primary keys, equivalent to PostgreSQL's SERIAL columns.
The AutoField is designed for auto-incrementing integer primary keys and automatically generates unique integer values for each record. Unlike IDField which uses BIGINT, AutoField uses INTEGER type and is suitable for applications that don't require the extended range of BIGINT values.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldprimary_key::Bool = true: Whether this field is the primary key for the tableauto_increment::Bool = true: Whether the field should auto-increment (generate values automatically)unique::Bool = true: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in forms (not applicable for auto fields)null::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this field (primary keys are automatically indexed)default::Union{Int64, Nothing} = nothing: Default value for the field (rarely used with auto-increment)editable::Bool = false: Whether the field should be editable in forms (typically false for auto fields)
Database Mapping
- PostgreSQL Type: INTEGER with SERIAL auto-increment
- Auto-increment: Supported through PostgreSQL's SERIAL type (sequence-based)
- Index: Automatically indexed as primary key
- Range: 32-bit signed integers (-2,147,483,648 to 2,147,483,647)
AutoField vs IDField Comparison
| Feature | AutoField | IDField |
|---|---|---|
| Database Type | INTEGER (SERIAL) | BIGINT (BIGSERIAL/IDENTITY) |
| Range | 32-bit (-2B to 2B) | 64-bit (-9Q to 9Q) |
| Storage | 4 bytes | 8 bytes |
| Generation | Sequence-based | Identity columns or sequence |
| Use Case | Small to medium apps | Large-scale applications |
When to Use AutoField vs IDField
Use AutoField when:
- Building small to medium-sized applications
- You don't expect more than ~2 billion records
- Storage efficiency is important (4 bytes vs 8 bytes per ID)
- Working with legacy systems that expect INTEGER primary keys
- Building lookup tables, categories, or reference data
Use IDField when:
- Building large-scale applications with potential for massive growth
- You need the extended range of 64-bit integers
- Working with data warehouses or analytics platforms
- Future-proofing against scale requirements
- Using modern PostgreSQL features like identity columns
PormG.Models.BigIntegerField — Method
BigIntegerField(; kwargs...)A field for storing 64-bit signed integers, equivalent to PostgreSQL's BIGINT columns.
The BigIntegerField stores large whole numbers within the 64-bit signed integer range (-9,223,372,036,854,775,808 to 9,223,372,036,854,775,807). It's ideal for large identifiers, timestamps, population counts, and other numeric data requiring extended range beyond regular integers.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldunique::Bool = false: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this fielddefault::Union{Int64, Nothing} = nothing: Default value for the fieldeditable::Bool = false: Whether the field should be editable in forms
Database Mapping
- PostgreSQL Type: BIGINT
- Storage: 8 bytes per value
- Range: -9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
- Index: Optional, recommended for frequently queried fields
Examples
Large identifier field:
Analytics = Models.Model(
_id = IDField(),
user_id = BigIntegerField(db_index=true),
session_id = BigIntegerField(),
timestamp_ms = BigIntegerField() # Unix timestamp in milliseconds
)Population and statistics:
Country = Models.Model(
_id = IDField(),
name = CharField(max_length=100),
population = BigIntegerField(null=true),
gdp_usd = BigIntegerField(null=true), # GDP in USD cents
area_sq_meters = BigIntegerField()
)Large external identifiers:
SocialMedia = Models.Model(
_id = IDField(),
user = ForeignKey("User"),
twitter_id = BigIntegerField(unique=true, null=true),
facebook_id = BigIntegerField(unique=true, null=true),
follower_count = BigIntegerField(default=0)
)Common Use Cases
- Large Identifiers: External API IDs, social media IDs
- Timestamps: Unix timestamps in milliseconds or microseconds
- Population Data: Country populations, large counts
- Financial Data: Large monetary values in smallest units
- Scientific Data: Large measurements, particle counts
- Analytics: Large user IDs, session identifiers
Migration Considerations
- From IntegerField: Safe upgrade, no data loss
- To IntegerField: Requires validation that all values fit in 32-bit range
- Index Changes: Indexes will be recreated with new size
- Application Code: May need updates if expecting different ranges
PormG.Models.BooleanField — Method
BooleanField(; kwargs...)A field for storing boolean (true/false) values, equivalent to PostgreSQL's BOOLEAN columns.
The BooleanField stores binary true/false values and is ideal for flags, switches, status indicators, and any field that represents a yes/no or on/off state. It maps directly to PostgreSQL's BOOLEAN type and Julia's Bool type.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldunique::Bool = false: Whether values in this field must be unique (rarely used with booleans)blank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this fielddefault::Union{Bool, Nothing} = nothing: Default value for the field (true or false)editable::Bool = false: Whether the field should be editable in forms
Examples
Basic boolean flags:
User = Models.Model(
_id = IDField(),
username = CharField(max_length=150),
is_active = BooleanField(default=true),
is_staff = BooleanField(default=false),
email_verified = BooleanField(default=false)
)Boolean Values and Conversion
The field handles various input formats:
- Julia Bool:
true,false - Integers:
1(true),0(false) - Strings:
"true","false","1","0","yes","no" - NULL: When
null=true, acceptsNULL/nothing
PormG.Models.CharField — Method
CharField(; kwargs...)A field for storing short to medium-length strings, equivalent to PostgreSQL's VARCHAR columns.
The CharField is the most commonly used field for storing textual data with a limited length. It maps to a PostgreSQL VARCHAR column and supports validation, indexing, choices, and various constraints. This field is ideal for names, titles, codes, and other string data with known maximum lengths.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldmax_length::Int = 250: Maximum number of characters allowed (1-255)unique::Bool = false: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this fielddb_column::Union{String, Nothing} = nothing: Custom database column name (defaults to field name)default::Union{String, Nothing} = nothing: Default value for the fieldchoices::Union{NTuple{N, Tuple{AbstractString, AbstractString}}, Nothing} = nothing: Restricted set of valid valueseditable::Bool = true: Whether the field should be editable in forms
Length Constraints
- Minimum: 1 character
- Maximum: 255 characters
- Validation: Automatically enforced at the field level
- Storage: Efficient variable-length storage in PostgreSQL
Examples
Basic string field:
User = Models.Model(
_id = IDField(),
username = CharField(max_length=150, unique=true),
first_name = CharField(max_length=50),
last_name = CharField(max_length=50)
)String field with choices (enumeration):
Order = Models.Model(
_id = IDField()
status = CharField(
max_length=20,
choices=(
("1", "Pending"),
("2", "Processing"),
("3", "Shipped"),
("4", "Delivered"),
("5", "Cancelled")
),
default="1"
)
customer_name = CharField(max_length=200)
)Optional field with custom database column:
Product = Models.Model(
_id = IDField(),
name = CharField(max_length=200),
sku = CharField(
max_length=50,
unique=true,
db_column="product_sku",
verbose_name="Stock Keeping Unit"
)
category = CharField(max_length=100, null=true, blank=true)
)Indexed field for performance:
Article = Models.Model(
_id = IDField(),
title = CharField(max_length=200, db_index=true),
slug = CharField(max_length=200, unique=true, db_index=true),
content = TextField()
)Choices Feature
The choices parameter allows you to restrict field values to a predefined set:
# Define choices as tuples of (value, display_name)
priority_choices = (
("low", "Low Priority"),
("medium", "Medium Priority"),
("high", "High Priority"),
("urgent", "Urgent")
)
Task = Models.Model(
_id = IDField(),
title = CharField(max_length=200),
priority = CharField(max_length=10, choices=priority_choices, default="medium")
)Choice Format Options:
- Tuple of Tuples:
(("value1", "Display 1"), ("value2", "Display 2")) - String Format:
"(value1, Display 1)(value2, Display 2)"
Default Values
- Static Default:
default="some_value" - Must Match Choices: If choices are specified, default must be one of the choice values
- Length Validation: Default value must not exceed
max_length
Database Column Naming
- Conventions: Follow PostgreSQL naming conventions (lowercase, underscores)
CharField vs TextField
| Feature | CharField | TextField |
|---|---|---|
| Length | Limited (1-255) | Unlimited |
| Database Type | VARCHAR | TEXT |
| Use Case | Short strings | Long content |
| Indexing | Efficient | Less efficient |
| Performance | Fast queries | Slower for large content |
Migration Considerations
- Increasing Length: Safe operation
- Decreasing Length: Requires data validation
- Adding Choices: Application-level change only
- Changing Column Name: Use
db_columnparameter
Notes
- The field uses VARCHAR type which is efficient for short to medium strings
- Choices are validated at the Julia application level, not in the database
- The
editable=truedefault makes this field suitable for user input forms - Database indexing is optional but recommended for frequently queried fields
- Compatible with PostgreSQL's text search and pattern matching features
See Also
TextFieldfor unlimited length text contentEmailFieldfor email address validation- Database design best practices for string field sizing
PormG.Models.DateField — Method
DateField(; kwargs...)A field for storing date values (without time), equivalent to PostgreSQL's DATE columns.
The DateField stores calendar dates in YYYY-MM-DD format and is ideal for birth dates, event dates, deadlines, and any date information that doesn't require time precision. It maps to PostgreSQL's DATE type and Julia's Date type.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldunique::Bool = false: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this fielddefault::Union{String, Nothing} = nothing: Default value for the field (YYYY-MM-DD format)editable::Bool = false: Whether the field should be editable in formsauto_now::Bool = false: Whether to automatically set to current date on every saveauto_now_add::Bool = false: Whether to automatically set to current date on creation only
Examples
Basic date fields:
User = Models.Model(
_id = IDField(),
username = CharField(max_length=150),
birth_date = DateField(null=true, blank=true),
join_date = DateField(auto_now_add=true),
last_login_date = DateField(null=true)
)Event and scheduling:
Event = Models.Model(
_id = IDField(),
title = CharField(max_length=200),
event_date = DateField(db_index=true),
registration_deadline = DateField(),
created_date = DateField(auto_now_add=true)
)Business dates:
Invoice = Models.Model(
_id = IDField(),
customer = ForeignKey("Customer"),
issue_date = DateField(auto_now_add=true),
due_date = DateField(),
paid_date = DateField(null=true, blank=true)
)Auto Date Features
autonowadd
Sets the date automatically when the record is first created:
created_date = DateField(auto_now_add=true)
# Automatically set to today's date on creation
# Never changes after initial creationauto_now
Updates the date automatically every time the record is saved:
last_modified_date = DateField(auto_now=true)
# Set to today's date on every save operation
# Useful for tracking last update datesDate Input Formats
The field accepts various input formats:
- Julia Date:
Date(2024, 7, 28) - DateTime:
DateTime(2024, 7, 28, 10, 30)(time ignored) - String ISO:
"2024-07-28" - String formats: Various date strings parseable by Julia
PormG.Models.DateTimeField — Method
DateTimeField(; kwargs...)A field for storing date and time values with timezone information.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{DateTime, Nothing, String}: Default value for the field. Can be a DateTime object, ISO string, ornothing. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:falseauto_now::Bool: Iftrue, automatically updates to current datetime on every save. Default:falseauto_now_add::Bool: Iftrue, automatically sets to current datetime when record is created. Default:falsetype::String: The database column type. Can be either"TIMESTAMPTZ"(default) or"TIMESTAMP". Default:"TIMESTAMPTZ"
Important Note: TIMESTAMPTZ vs TIMESTAMP
By default, DateTimeField uses TIMESTAMPTZ.
- TIMESTAMPTZ (Recommended): Stores values in UTC internally and converts them to your session's timezone upon retrieval. This ensures consistency across different geographical regions.
- TIMESTAMP: Stores the exact date and time provided without any timezone conversion.
Examples
# Basic datetime field
created_at = DateTimeField()
# Auto-timestamp fields
created_at = DateTimeField(auto_now_add=true)
updated_at = DateTimeField(auto_now=true)
# Indexed datetime for queries
event_time = DateTimeField(db_index=true, verbose_name="Event Timestamp")
# With default value
scheduled_at = DateTimeField(default=DateTime(2024, 1, 1, 12, 0, 0))
# Optional datetime field
deadline = DateTimeField(null=true, blank=true)```PormG.Models.DecimalField — Method
DecimalField(; kwargs...)A field for storing decimal numbers with fixed precision and scale.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{Float64, Nothing}: Default value for the field. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:falsemax_digits::Int: Maximum number of digits allowed (including decimal places). Default:10decimal_places::Int: Number of decimal places to store. Default:2
Examples
# Currency field (2 decimal places)
price = DecimalField(max_digits=10, decimal_places=2)
# High precision scientific values
measurement = DecimalField(max_digits=15, decimal_places=6)
# Percentage with 4 decimal places
rate = DecimalField(max_digits=7, decimal_places=4, default=0.0)
# Financial calculation field
amount = DecimalField(
max_digits=12,
decimal_places=2,
verbose_name="Transaction Amount",
db_index=true
)
# Optional decimal field
discount = DecimalField(
max_digits=5,
decimal_places=2,
null=true,
blank=true
)PormG.Models.EmailField — Method
EmailField(; kwargs...)A field for storing and validating email addresses.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{String, Nothing}: Default email address. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:false
Examples
# Basic email field
email = EmailField()
# Unique email for user accounts
user_email = EmailField(unique=true, verbose_name="User Email")
# Optional contact email
contact_email = EmailField(null=true, blank=true)
# Email with default value
notification_email = EmailField(default="admin@example.com")
# Indexed email for fast lookups
primary_email = EmailField(
unique=true,
db_index=true,
)PormG.Models.FloatField — Method
FloatField(; kwargs...)A field for storing floating-point numbers with double precision.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{Float64, String, Int64, Nothing}: Default value for the field. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:false
Examples
# Basic float field
temperature = FloatField()
# Scientific measurement with default
ph_level = FloatField(default=7.0)
# Optional measurement
weight = FloatField(null=true)PormG.Models.ForeignKey — Method
ForeignKey(to::Union{String, PormGModel}; kwargs...)A field that creates a many-to-one relationship to another model, similar to Django's ForeignKey.
The ForeignKey field represents a relationship where many records in the current model can reference a single record in the target model. It creates a foreign key constraint in the database and enables efficient querying of related data.
Required Arguments
to::Union{String, PormGModel}: The target model that this field references. Can be either:- A string with the model name (e.g., "User", "Category")
- A direct reference to a PormGModel instance
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldprimary_key::Bool = false: Whether this field is the primary key (rarely used with ForeignKey)unique::Bool = false: Whether values must be unique (creates a one-to-one relationship if true)blank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = true: Whether to create a database index on this field (recommended for performance)default::Union{Int64, Nothing} = nothing: Default value for the field (ID of the referenced record)editable::Bool = false: Whether the field should be editable in formspk_field::Union{String, Symbol, Nothing} = nothing: Which field in the target model to reference (defaults to primary key)on_delete::Union{Function, String, Nothing} = nothing: Action when the referenced object is deletedon_update::Union{String, Nothing} = nothing: Action when the referenced object's key is updateddeferrable::Bool = false: Whether the constraint check can be deferred until transaction commitinitially_deferred::Bool = false: Whether constraint checking is initially deferredhow::Union{String, Nothing} = nothing: Join type for queries ("INNER JOIN", "LEFT JOIN", etc.)related_name::Union{String, Nothing} = nothing: Name for the reverse relationdb_constraint::Bool = true: Whether to create a database foreign key constraint
Database Mapping
- PostgreSQL Type: BIGINT with foreign key constraint
- Constraint: Creates
FOREIGN KEYconstraint linking to target table - Index: Automatically indexed for query performance
On Delete Options
The on_delete parameter controls what happens when the referenced object is deleted:
CASCADE: Delete this object when referenced object is deletedRESTRICT: Prevent deletion of referenced object if this object existsSET_NULL: Set this field to NULL (requiresnull=true)SET_DEFAULT: Set this field to its default value (requiresdefaultto be set)SET: Set this field to a specific valuePROTECT: Raise an error to prevent deletionDO_NOTHING: Take no action (may cause database integrity errors)
Examples
Basic foreign key relationship:
Article = Models.Model(
_id = IDField()
title = CharField(max_length=200)
author = ForeignKey("User")
category = ForeignKey("Category", on_delete=CASCADE)
)Foreign key allowing NULL values:
Product = Models.Model(
_id = IDField()
_id = IDField()
name = CharField(max_length=100)
category = ForeignKey("Category", null=true, blank=true, on_delete=SET_NULL)
)Multiple foreign keys to same model (requires related_name):
Message = Models.Model(
_id = IDField()
sender = ForeignKey("User", related_name="sent_messages")
recipient = ForeignKey("User", related_name="received_messages")
content = TextField()
)Related Names and Reverse Relations
- If
related_nameis not specified, PormG automatically generates one - When multiple ForeignKeys point to the same model,
related_namemust be explicitly set - The related name allows querying from the target model back to this model
- If you can't remember the related name, you can type
your_query.objects.related_objectsoryour_model.related_objectsto see all related names
Database Constraints
- When
db_constraint=true(default), creates actual foreign key constraints in PostgreSQL - When
db_constraint=false, no database constraint is created (useful for legacy databases) - Database constraints ensure referential integrity but may impact performance
Validation
- The
toparameter must be a valid model name or PormGModel instance - All boolean parameters are validated for type safety
- The
on_deleteparameter is validated against allowed values - Invalid parameters trigger warnings but don't cause errors
Notes
- The field stores the primary key value of the referenced object
- Uses BIGINT type to match IDField primary keys
- Supports deferred constraint checking for complex transactions
- Compatible with PostgreSQL's foreign key features
See Also
- Django's ForeignKey documentation for conceptual understanding
PormG.Models.IDField — Method
IDField(; kwargs...)A field type for auto-incrementing integer primary keys, equivalent to PostgreSQL's BIGSERIAL or GENERATED AS IDENTITY columns.
The IDField is typically used as the primary key for models and automatically generates unique integer values for each record. It maps to a PostgreSQL BIGINT column with auto-increment capabilities.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldprimary_key::Bool = true: Whether this field is the primary key for the tableauto_increment::Bool = true: Whether the field should auto-increment (generate values automatically)unique::Bool = true: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in forms (not applicable for ID fields)null::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = true: Whether to create a database index on this fielddefault::Union{Int64, Nothing} = nothing: Default value for the field (rarely used with auto-increment)editable::Bool = false: Whether the field should be editable in forms (typically false for ID fields)generated::Bool = true: Whether to use PostgreSQL's GENERATED AS IDENTITY featuregenerated_always::Bool = false: Whether to use GENERATED ALWAYS AS IDENTITY (stricter than regular GENERATED)
Database Mapping
- PostgreSQL Type: BIGINT with GENERATED AS IDENTITY or GENERATED ALWAYS AS IDENTITY
- Auto-increment: Supported through PostgreSQL's identity columns
- Index: Automatically indexed as primary key
Examples
Basic usage (most common):
User = Models.Model(
_id::PormGField = IDField()
name::PormGField = CharField(max_length=100)
email::PormGField = EmailField()
)Using GENERATED ALWAYS (stricter identity):
Order = Models.Model(
_id::PormGField = IDField(generated_always=true)
customer_id::PormGField = ForeignKey("Customer")
order_date::PormGField = DateTimeField()
)Notes
- The
IDFieldis designed to be the primary key and should typically be the first field in your model - Values are automatically generated by the database, so you don't need to provide them when creating records
- The field uses BIGINT type to support large ranges of ID values
- When
generated_always=true, the database will reject any attempts to manually insert ID values - This field type is PostgreSQL-specific and optimized for PormG's PostgreSQL backend
Validation
- All boolean parameters are validated to ensure type safety
- The
verbose_namemust be a String or nothing - The
defaultvalue, if provided, must be convertible to Int64 - Invalid parameters will trigger warnings but won't cause errors (they'll be ignored)
PormG.Models.ImageField — Method
ImageField(; kwargs...)A field for storing image file references and metadata.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{String, Nothing}: Default image path or URL. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:false
Examples
# Basic image field
avatar = ImageField()
# Product image with default
product_image = ImageField(
default="/static/images/default-product.jpg",
verbose_name="Product Image"
)
# Optional profile picture
profile_pic = ImageField(null=true)
# Unique banner image
banner = ImageField(
unique=true,
db_index=true
)PormG.Models.IntegerField — Method
IntegerField(; kwargs...)A field for storing 32-bit signed integers, equivalent to PostgreSQL's INTEGER columns.
The IntegerField stores whole numbers within the 32-bit signed integer range (-2,147,483,648 to 2,147,483,647). It's ideal for counts, quantities, ratings, and other numeric data that doesn't require decimal places or extremely large values.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldunique::Bool = false: Whether values in this field must be unique across all recordsblank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = false: Whether to create a database index on this fielddefault::Union{Int64, Nothing} = nothing: Default value for the fieldeditable::Bool = false: Whether the field should be editable in forms
Examples
Basic integer field:
Product = Models.Model(
_id = IDField(),
name = CharField(max_length=200),
quantity = IntegerField(default=0),
price_cents = IntegerField() # Store price in cents to avoid decimals
)Integer field with constraints:
User = Models.Model(
_id = IDField(),
username = CharField(max_length=150, unique=true),
age = IntegerField(null=true, blank=true),
score = IntegerField(default=0, db_index=true)
)Rating system:
Review = Models.Model(
_id = IDField(),
product = ForeignKey("Product"),
rating = IntegerField(default=5), # 1-5 star rating
helpful_votes = IntegerField(default=0)
)Validation and Constraints
- Range: Automatically validates within INTEGER bounds
- Type: Accepts integers, numeric strings (converted automatically)
- Default: Must be an integer or convertible to integer
- Null: When
null=true, accepts NULL values
Migration Considerations
- Range Changes: Changing to BigIntegerField is safe
- Adding Constraints: Adding uniqueness or indexes is safe
- Default Values: Can be added or modified safely
- Null Constraints: Removing null constraint requires data validation
PormG.Models.Model_to_str — Method
Converts a model object to a string representation to create the model.
Arguments
Model_to_str(model::Union{Model_Type, PormGModel}; contants_julia::Vector{String}=reserved_words)::Stringmodel::Union{Model_Type, PormGModel}: The model object to convert.contants_julia::Vector{String}=reserved_words: A vector of reserved words in Julia.
Returns
String: The string representation of the model object.
Examples
users = Models.Model("users",
name = Models.CharField(),
email = Models.CharField(),
age = Models.IntegerField()
)PormG.Models.OneToOneField — Method
OneToOneField(to::Union{String, PormGModel}; kwargs...)A field that creates a one-to-one relationship to another model, similar to Django's OneToOneField.
The OneToOneField represents a strict one-to-one relationship where each record in the current model corresponds to exactly one record in the target model, and vice versa. It's essentially a ForeignKey with a unique constraint that ensures no two records can reference the same target record.
Required Arguments
to::Union{String, PormGModel}: The target model that this field references. Can be either:- A string with the model name (e.g., "UserProfile", "Settings")
- A direct reference to a PormGModel instance
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldprimary_key::Bool = false: Whether this field is the primary key (rarely used with OneToOneField)unique::Bool = true: Whether values must be unique (always true for one-to-one relationships)blank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valuesdb_index::Bool = true: Whether to create a database index on this field (recommended for performance)default::Union{Int64, Nothing} = nothing: Default value for the field (ID of the referenced record)editable::Bool = false: Whether the field should be editable in formspk_field::Union{String, Symbol, Nothing} = nothing: Which field in the target model to reference (defaults to primary key)on_delete::Union{Function, String, Nothing} = nothing: Action when the referenced object is deletedon_update::Union{String, Nothing} = nothing: Action when the referenced object's key is updateddeferrable::Bool = false: Whether the constraint check can be deferred until transaction commitinitially_deferred::Bool = false: Whether constraint checking is initially deferredhow::Union{String, Nothing} = nothing: Join type for queries ("INNER JOIN", "LEFT JOIN", etc.)related_name::Union{String, Nothing} = nothing: Name for the reverse relationdb_constraint::Bool = true: Whether to create a database foreign key constraint
Database Mapping
- PostgreSQL Type: BIGINT with unique foreign key constraint
- Constraint: Creates
FOREIGN KEYconstraint withUNIQUEconstraint - Index: Automatically indexed for query performance and uniqueness enforcement
One-to-One Relationship Characteristics
- Uniqueness: Each target record can only be referenced by one record in the current model
- Bidirectional: The relationship can be traversed in both directions
- Inheritance: Often used to extend models without modifying the original table
- Profile Pattern: Commonly used for user profiles, settings, or detailed information tables
On Delete Options
The on_delete parameter controls what happens when the referenced object is deleted:
CASCADE: Delete this object when referenced object is deletedRESTRICT: Prevent deletion of referenced object if this object existsSET_NULL: Set this field to NULL (requiresnull=true)SET_DEFAULT: Set this field to its default value (requiresdefaultto be set)SET: Set this field to a specific valuePROTECT: Raise an error to prevent deletionDO_NOTHING: Take no action (may cause database integrity errors)
Examples
Basic one-to-one relationship (User Profile pattern):
User = Models.Model(
_id = IDField()
username = CharField(max_length=150, unique=true)
email = EmailField()
)
UserProfile = Models.Model(
_id = IDField()
user = OneToOneField("User", on_delete=CASCADE)
bio = TextField(blank=true)
avatar = ImageField(blank=true)
birth_date = DateField(null=true, blank=true)
)One-to-one with null values allowed:
Employee = Models.Model(
_id = IDField()
name = CharField(max_length=100)
department = CharField(max_length=50)
)
EmployeeSettings = Models.Model(
_id = IDField()
employee = OneToOneField("Employee", null=true, blank=true, on_delete=SET_NULL)
email_notifications = BooleanField(default=true)
theme_preference = CharField(max_length=20, default="light")
)Extending a model without modifying it:
Product = Models.Model(
_id = IDField()
name = CharField(max_length=200)
price = DecimalField(max_digits=10, decimal_places=2)
)
ProductDetails = Models.Model(
_id = IDField()
product = OneToOneField("Product", on_delete=CASCADE, related_name="details")
detailed_description = TextField()
technical_specs = TextField()
warranty_info = TextField()
)Database Constraints vs. Unique ForeignKey
OneToOneField is equivalent to:
# These are functionally identical:
user = OneToOneField("User")
user = ForeignKey("User", unique=true)However, OneToOneField is more explicit about the intended relationship type and provides better semantic meaning.
Validation
- The
toparameter must be a valid model name or PormGModel instance - All boolean parameters are validated for type safety
- The
on_deleteparameter is validated against allowed values - Uniqueness is automatically enforced at the database level
- Invalid parameters trigger warnings but don't cause errors
See Also
- Django's OneToOneField documentation for conceptual understanding
- Database normalization principles for when to use one-to-one relationships
PormG.Models.PasswordField — Method
PasswordField(; kwargs...)A field for securely storing passwords using PBKDF2-SHA256 hashing (Django-compatible).
The PasswordField stores hashed passwords in a format compatible with Django's authentication system. Passwords are never stored in plain text - they are automatically hashed using PBKDF2-SHA256 with a randomly generated salt and configurable iterations.
Keyword Arguments
verbose_name::Union{String, Nothing} = nothing: A human-readable name for the fieldblank::Bool = false: Whether the field can be left blank in formsnull::Bool = false: Whether the database column can store NULL valueseditable::Bool = true: Whether the field should be editable in formsmax_length::Int = 128: Maximum length for stored hash (Django default)auto_hash::Bool = true: Whether to automatically hash passwords on insert/update
Database Mapping
- PostgreSQL Type: VARCHAR(128)
- Storage Format:
pbkdf2_sha256$iterations$salt$base64hash - Index: Not indexed by default (passwords shouldn't be queried)
Stored Format
The password is stored in Django-compatible format:
pbkdf2_sha256$720000$salt$base64encodedHashWhere:
pbkdf2_sha256: Algorithm identifier720000: Number of iterationssalt: Random 22-character saltbase64encodedHash: The derived key in base64
Examples
Basic password field:
# Define a User model with password
User = Models.Model(
_id = Models.IDField(),
username = Models.CharField(max_length=150, unique=true),
email = Models.EmailField(unique=true),
password = Models.PasswordField()
)Creating a user with hashed password:
import PormG.Models: make_password, check_password
# Hash the password before storing
hashed = make_password("securePassword123!")
query = M.User |> object
query.bulk_insert(
username = "ayrton_senna",
email = "senna@mclaren.com",
password = hashed
)Verifying a password during login:
import PormG.Models: check_password
# Fetch user from database
user_query = M.User |> object
user_query.filter("username" => "ayrton_senna")
users = user_query |> list
if !isempty(users)
user = users[1]
# Verify the password
if check_password("securePassword123!", user[:password])
println("Login successful!")
else
println("Invalid password")
end
endRelated Functions
make_password(raw): Hash a plain text passwordcheck_password(raw, encoded): Verify a password against stored hashpassword_needs_upgrade(encoded): Check if hash needs re-hashing
Security Notes
- Never store plain text passwords - always use
make_password() - The default 720000 iterations provides strong security (Django 4.2+ default)
- Salts are automatically generated per-password
- Use
check_password()for verification (includes timing attack protection) - Consider implementing password upgrade on login if using older hashes
Migration from Django
If you're migrating from a Django application, password hashes are fully compatible. Users can continue to log in without any password reset.
See Also
CharFieldfor generic string storage- Django's password management documentation
PormG.Models.TextField — Method
TextField(; kwargs...)A field for storing large amounts of text without length restrictions.
Keyword Arguments
verbose_name::Union{String, Nothing}: Human-readable name for the field. Default:nothingunique::Bool: Iftrue, ensures field values are unique across the table. Default:falseblank::Bool: Iftrue, allows empty values in forms/validation. Default:falsenull::Bool: Iftrue, allows NULL values in the database. Default:falsedb_index::Bool: Iftrue, creates a database index for faster queries. Default:falsedefault::Union{String, Nothing}: Default text content. Default:nothingeditable::Bool: Iftrue, field can be edited in forms. Default:false
Examples
# Basic text field for long content
description = TextField()
# Blog post content
content = TextField(blank=true)
# Optional notes field
notes = TextField(null=true, blank=true)
# Indexed text field for search
searchable_content = TextField(
db_index=true
)
# Text field with default content
template = TextField(
default="Enter your text here...",
verbose_name="Template Content"
)PormG.Models.are_model_fields_equal — Method
aremodelfieldsequal(newmodel::PormGModel, old_model::PormGModel) :: Bool
Compares the fields of two PormGModel instances to determine if they are equal.
PormG.Models.ensure_model_initialized — Method
ensure_model_initialized(model::PormGModel)Checks if a model has been initialized (i.e., has a connect_key that exists in config). If not, attempts self-healing by:
- Remapping stale path-based keys to current session alias keys
- Re-registering via REGISTERED_MODULES
- Scanning loaded modules for pormginitpath markers
- Using the model's own
_modulereference (survives precompilation)
PormG.Models.format_string — Method
format_string(x)Format the input x as a string if it is of type String, otherwise return x as is.
PormG.Models.get_all_models — Method
Returns a vector containing all the models defined in the given module.
Arguments
get_all_models(modules::Module; symbol::Bool=false)::Vector{Union{Symbol, PormGModel}}modules::Module: The module to search for models.symbol::Bool=false: Iftrue, returns the model names as symbols. Iffalse, returns the model instances.
Returns
A vector containing all the models defined in the module.
PormG.Models.validate_default — Method
validatedefault(default, expectedtype::Type, field_name::String, converter::Function)
Validate the default value for a field based on the expected type.
Arguments
default: The default value to be validated.expected_type::Type: The expected type for the default value.field_name::String: The name of the field being validated.converter::Function: A function used to convert the default value if it is not of the expected type.
Returns
- If the default value is of the expected type, it is returned as is.
- If the default value can be converted to the expected type using the provided converter function, the converted value is returned.
- If the default value is neither of the expected type nor convertible to it, an
ArgumentErroris thrown.