.. rst-class:: emphasize-children
All models should be derived from Model. To start describing the models, import Model from oxen.models.
from oxen import ModelWith that start describing the models
from oxen import Model
from oxen.fields import CharField, IntField, DateTimeField, DecimalField, BooleanField
class Tournament(Model):
id = IntField(primary_key=True)
name = CharField(max_length=255)
created = DateTimeField(auto_now_add=True)
is_active = BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
table_name = "tournaments"
class Event(Model):
id = IntField(primary_key=True)
name = CharField(max_length=255)
tournament_id = IntField() # Foreign key reference
participants = CharField(max_length=1000) # JSON field for many-to-many
modified = DateTimeField(auto_now=True)
prize = DecimalField(max_digits=10, decimal_places=2, null=True)
def __str__(self):
return self.name
class Meta:
table_name = "events"
class Team(Model):
id = IntField(primary_key=True)
name = CharField(max_length=255)
def __str__(self):
return self.name
class Meta:
table_name = "teams"Let's look at the details of what we accomplished here:
class Tournament(Model):Every model should be derived from Model or its subclasses. Custom Model subclasses can be created in the following way:
class AbstractTournament(Model):
id = IntField(primary_key=True)
name = CharField(max_length=255)
created = DateTimeField(auto_now_add=True)
class Meta:
abstract = True
def __str__(self):
return self.nameThis model will not affect the schema, but it will be available for inheritance.
Further we have field DateTimeField(auto_now=True). Options auto_now and auto_now_add work like Django's options.
OxenORM models support several Meta options:
class User(Model):
id = IntField(primary_key=True)
name = CharField(max_length=100)
email = CharField(max_length=255, unique=True)
class Meta:
table_name = "users" # Custom table name
abstract = False # Whether this is an abstract model
ordering = ["name"] # Default orderingIn OxenORM, every model must have a primary key.
That primary key will be accessible through a reserved field pk which will be an alias of whichever field has been nominated as a primary key.
That alias field can be used as a field name when doing filtering e.g. .filter(pk=...) etc…
Note
We currently support single (non-composite) primary keys of any indexable field type, but only these field types are recommended:
IntField
BigIntField
CharField
UUIDFieldOne must define a primary key by setting the primary_key parameter to True.
OxenORM provides comprehensive CRUD operations:
# Create
user = await User.create(name="John Doe", email="john@example.com")
# Read
user = await User.get(id=1)
users = await User.all()
active_users = await User.filter(is_active=True)
# Update
user.name = "Jane Doe"
await user.save()
# Delete
await user.delete()OxenORM supports efficient bulk operations:
# Bulk create
users_to_create = [
User(name=f"User {i}", email=f"user{i}@example.com")
for i in range(100)
]
created_users = await User.bulk_create(users_to_create)
# Bulk update
for user in users:
user.is_active = False
updated_count = await User.bulk_update(users, ['is_active'])
# Bulk delete
deleted_count = await User.filter(is_active=False).delete()OxenORM provides a rich query API:
# Filtering
users = await User.filter(age__gte=18, is_active=True)
# Excluding
users = await User.exclude(is_active=False)
# Ordering
users = await User.order_by('name', '-created_at')
# Limiting and offsetting
users = await User.limit(10).offset(20)
# Counting
user_count = await User.count()
active_count = await User.filter(is_active=True).count()
# Existence checks
has_users = await User.exists()
has_john = await User.filter(name__contains="John").exists()
# First record
first_user = await User.first()
first_active = await User.filter(is_active=True).first()OxenORM supports Django-style field lookups:
# Exact match
users = await User.filter(name="John")
# Case-insensitive contains
users = await User.filter(name__icontains="john")
# Starts with
users = await User.filter(name__startswith="John")
# Ends with
users = await User.filter(name__endswith="Doe")
# Greater than, less than
users = await User.filter(age__gte=18, age__lte=65)
# In list
users = await User.filter(name__in=["John", "Jane", "Bob"])
# Is null
users = await User.filter(email__isnull=True)
# Is not null
users = await User.filter(email__isnull=False)OxenORM supports complex queries using Q objects:
from oxen.queryset import Q
# OR conditions
users = await User.filter(
Q(name__contains="John") | Q(email__contains="john")
)
# AND conditions
users = await User.filter(
Q(age__gte=18) & Q(is_active=True)
)
# NOT conditions
users = await User.filter(
~Q(is_active=False)
)OxenORM supports database aggregations:
from oxen.queryset import Count, Avg, Max, Min, Sum
# Count
user_count = await User.count()
# Average age
avg_age = await User.aggregate(avg_age=Avg('age'))
# Maximum age
max_age = await User.aggregate(max_age=Max('age'))
# Sum of values
total_value = await Order.aggregate(total=Sum('amount'))OxenORM supports database transactions:
from oxen import connect
async def transfer_money(from_user_id, to_user_id, amount):
async with connect.transaction() as tx:
# Deduct from source account
from_user = await User.get(id=from_user_id)
from_user.balance -= amount
await from_user.save()
# Add to destination account
to_user = await User.get(id=to_user_id)
to_user.balance += amount
await to_user.save()OxenORM supports using multiple databases:
from oxen import MultiDatabaseManager
# Initialize multi-database manager
manager = MultiDatabaseManager({
'primary': 'postgresql://user:pass@localhost/primary',
'analytics': 'mysql://user:pass@localhost/analytics',
'cache': 'sqlite://:memory:'
})
# Use specific database for operations
user = await User.objects.using('primary').create(name="User")
event = await AnalyticsEvent.objects.using('analytics').create(event="page_view")OxenORM supports model validation:
from oxen.validators import MinValueValidator, MaxValueValidator
class User(Model):
id = IntField(primary_key=True)
name = CharField(max_length=100)
age = IntField(validators=[MinValueValidator(0), MaxValueValidator(120)])
email = CharField(max_length=255, unique=True)
def clean(self):
# Custom validation
if self.age < 18 and self.email.endswith('@adult.com'):
raise ValidationError("Age restriction for adult content")OxenORM provides several performance optimization features:
# Select only specific fields
users = await User.filter(is_active=True).only('id', 'name')
# Use bulk operations for large datasets
users = await User.bulk_create(large_user_list)
# Use transactions for multiple operations
async with connect.transaction() as tx:
# Multiple operations in single transaction
passSee :ref:`performance` for detailed performance optimization guides.