Model API Reference
Model API Reference
Complete reference for PyFrame’s data models and ORM system.
Base Model Class
Model
The base class for all PyFrame data models.
1
2
3
4
5
6
7
8
9
from pyframe import Model, Field, FieldType
class User(Model):
name = Field(FieldType.STRING, max_length=100)
email = Field(FieldType.EMAIL, unique=True)
class Meta:
table_name = 'users'
indexes = ['email']
Class Methods
create(**kwargs) -> Model
Create a new model instance and save to database.
Parameters:
**kwargs
- Field values for the new instance
Returns:
Model
- The created model instance
Example:
1
2
3
4
user = await User.create(
name='John Doe',
email='john@example.com'
)
get(id) -> Model
Get a model instance by primary key.
Parameters:
id
- Primary key value
Returns:
Model
- The model instance
Raises:
DoesNotExist
- If no instance found
Example:
1
user = await User.get(123)
filter(**kwargs) -> QuerySet
Filter model instances by field values.
Parameters:
**kwargs
- Field filters
Returns:
QuerySet
- Query set for chaining
Example:
1
2
active_users = await User.filter(is_active=True)
recent_users = await User.filter(created_at__gte=datetime.now() - timedelta(days=7))
all() -> List[Model]
Get all model instances.
Returns:
List[Model]
- All model instances
Example:
1
all_users = await User.all()
count() -> int
Count total number of instances.
Returns:
int
- Number of instances
Example:
1
user_count = await User.count()
exists() -> bool
Check if any instances exist.
Returns:
bool
- True if instances exist
Example:
1
has_users = await User.exists()
bulk_create(instances) -> List[Model]
Create multiple instances in batch.
Parameters:
instances: List[Model]
- Model instances to create
Returns:
List[Model]
- Created instances with IDs
Example:
1
2
3
4
5
users = [
User(name='Alice', email='alice@example.com'),
User(name='Bob', email='bob@example.com')
]
created_users = await User.bulk_create(users)
bulk_update(instances, fields) -> None
Update multiple instances in batch.
Parameters:
instances: List[Model]
- Model instances to updatefields: List[str]
- Fields to update
Example:
1
2
3
4
for user in users:
user.is_verified = True
await User.bulk_update(users, ['is_verified'])
Instance Methods
save(update_fields=None) -> None
Save the model instance to database.
Parameters:
update_fields: List[str]
- Optional list of fields to update
Example:
1
2
3
4
5
user.name = 'Jane Doe'
await user.save()
# Update only specific fields
await user.save(update_fields=['name'])
delete() -> None
Delete the model instance from database.
Example:
1
await user.delete()
refresh() -> None
Reload the instance from database.
Example:
1
await user.refresh()
to_dict() -> dict
Convert instance to dictionary.
Returns:
dict
- Instance data as dictionary
Example:
1
2
user_data = user.to_dict()
# {'id': 1, 'name': 'John', 'email': 'john@example.com'}
update(**kwargs) -> None
Update specific fields.
Parameters:
**kwargs
- Field values to update
Example:
1
await user.update(name='New Name', is_active=False)
Properties
pk
Primary key value of the instance.
1
user_id = user.pk
is_saved
Whether the instance has been saved to database.
1
2
if user.is_saved:
print("User exists in database")
Field Types
Field
Base field class for model attributes.
1
2
3
4
5
from pyframe import Field, FieldType
class MyModel(Model):
name = Field(FieldType.STRING, max_length=100, required=True)
age = Field(FieldType.INTEGER, min_value=0, default=0)
Parameters
field_type: FieldType
- The type of fieldrequired: bool
- Whether field is required (default:False
)default: Any
- Default value for fieldnull: bool
- Whether field can be NULL (default:False
)blank: bool
- Whether field can be blank in forms (default:False
)unique: bool
- Whether field values must be unique (default:False
)db_index: bool
- Whether to create database index (default:False
)validators: List[callable]
- Custom validation functions
Field Types
FieldType.STRING
Text field with optional length limit.
Parameters:
max_length: int
- Maximum string lengthmin_length: int
- Minimum string length
1
name = Field(FieldType.STRING, max_length=100, min_length=2)
FieldType.INTEGER
Integer field with optional range validation.
Parameters:
min_value: int
- Minimum allowed valuemax_value: int
- Maximum allowed value
1
age = Field(FieldType.INTEGER, min_value=0, max_value=150)
FieldType.FLOAT
Floating point number field.
Parameters:
min_value: float
- Minimum allowed valuemax_value: float
- Maximum allowed value
1
rating = Field(FieldType.FLOAT, min_value=0.0, max_value=5.0)
FieldType.DECIMAL
Fixed-precision decimal field.
Parameters:
max_digits: int
- Total number of digitsdecimal_places: int
- Number of decimal places
1
price = Field(FieldType.DECIMAL, max_digits=10, decimal_places=2)
FieldType.BOOLEAN
Boolean true/false field.
1
is_active = Field(FieldType.BOOLEAN, default=True)
FieldType.DATE
Date field (year, month, day).
1
birth_date = Field(FieldType.DATE)
FieldType.DATETIME
Date and time field.
Parameters:
auto_now: bool
- Update on every saveauto_now_add: bool
- Set on creation only
1
2
created_at = Field(FieldType.DATETIME, auto_now_add=True)
updated_at = Field(FieldType.DATETIME, auto_now=True)
FieldType.TIME
Time field (hour, minute, second).
1
meeting_time = Field(FieldType.TIME)
FieldType.EMAIL
Email address field with validation.
1
email = Field(FieldType.EMAIL, unique=True)
FieldType.URL
URL field with validation.
1
website = Field(FieldType.URL, null=True)
FieldType.UUID
UUID field for unique identifiers.
1
uuid_id = Field(FieldType.UUID, primary_key=True)
FieldType.JSON
JSON data field.
1
metadata = Field(FieldType.JSON, default=dict)
FieldType.TEXT
Large text field.
1
description = Field(FieldType.TEXT, null=True)
FieldType.BINARY
Binary data field.
1
file_content = Field(FieldType.BINARY)
FieldType.CHOICE
Field with predefined choices.
Parameters:
choices: List[tuple]
- Available choices as (value, label) tuples
1
2
3
4
5
status = Field(FieldType.CHOICE, choices=[
('draft', 'Draft'),
('published', 'Published'),
('archived', 'Archived')
])
FieldType.SLUG
URL-safe slug field.
Parameters:
auto_generate: bool
- Auto-generate from another fieldsource_field: str
- Field to generate slug from
1
slug = Field(FieldType.SLUG, auto_generate=True, source_field='title')
Relationship Fields
FieldType.FOREIGN_KEY
Reference to another model.
Parameters:
to: Model
- Target model classon_delete: str
- Deletion behavior (‘CASCADE’, ‘SET_NULL’, ‘PROTECT’)related_name: str
- Name for reverse relation
1
2
3
4
5
6
7
8
class Post(Model):
author = Field(FieldType.FOREIGN_KEY, to=User, on_delete='CASCADE')
category = Field(FieldType.FOREIGN_KEY, to='Category', related_name='posts')
# Usage
post = await Post.get(1)
author = await post.author # Get related user
author_posts = await author.posts.all() # Reverse relation
FieldType.ONE_TO_ONE
One-to-one relationship with another model.
Parameters:
to: Model
- Target model classon_delete: str
- Deletion behavior
1
2
3
4
5
6
7
8
class UserProfile(Model):
user = Field(FieldType.ONE_TO_ONE, to=User, on_delete='CASCADE')
bio = Field(FieldType.TEXT)
# Usage
profile = await UserProfile.get(user_id=1)
user = await profile.user
user_profile = await user.profile
FieldType.MANY_TO_MANY
Many-to-many relationship with another model.
Parameters:
to: Model
- Target model classthrough: Model
- Intermediate model (optional)related_name: str
- Name for reverse relation
1
2
3
4
5
6
7
8
9
10
11
12
13
class Post(Model):
title = Field(FieldType.STRING, max_length=200)
tags = Field(FieldType.MANY_TO_MANY, to='Tag')
class Tag(Model):
name = Field(FieldType.STRING, max_length=50)
# Usage
post = await Post.get(1)
await post.tags.add(tag1, tag2) # Add tags
await post.tags.remove(tag1) # Remove tag
await post.tags.set([tag2, tag3]) # Replace all tags
tags = await post.tags.all() # Get all tags
QuerySet API
QuerySet
Query set for building and executing database queries.
1
2
3
# QuerySet is returned by filter operations
users = User.filter(is_active=True) # Returns QuerySet
active_users = await users # Execute query
Methods
filter(**kwargs) -> QuerySet
Add filter conditions.
1
users = await User.filter(is_active=True, age__gte=18)
exclude(**kwargs) -> QuerySet
Exclude records matching conditions.
1
users = await User.exclude(is_banned=True)
order_by(*fields) -> QuerySet
Order results by fields.
1
users = await User.order_by('name', '-created_at') # - for descending
limit(count) -> QuerySet
Limit number of results.
1
recent_users = await User.order_by('-created_at').limit(10)
offset(count) -> QuerySet
Skip number of results.
1
page_2_users = await User.offset(20).limit(10)
select_related(*fields) -> QuerySet
Eagerly load related objects (JOIN).
1
2
posts = await Post.select_related('author', 'category')
# No additional query needed for post.author
prefetch_related(*fields) -> QuerySet
Prefetch related objects (separate queries).
1
2
posts = await Post.prefetch_related('tags')
# All tags loaded in one additional query
distinct(*fields) -> QuerySet
Return distinct results.
1
unique_categories = await Post.values('category').distinct()
values(*fields) -> QuerySet
Return dictionaries instead of model instances.
1
2
user_data = await User.values('id', 'name', 'email')
# [{'id': 1, 'name': 'John', 'email': 'john@example.com'}, ...]
values_list(*fields, flat=False) -> QuerySet
Return tuples instead of model instances.
1
2
3
4
5
user_names = await User.values_list('name', flat=True)
# ['John', 'Jane', 'Bob', ...]
user_pairs = await User.values_list('id', 'name')
# [(1, 'John'), (2, 'Jane'), (3, 'Bob'), ...]
aggregate(**kwargs) -> dict
Perform aggregation functions.
1
2
3
4
5
6
7
8
from pyframe.models import Count, Sum, Avg, Max, Min
stats = await Post.aggregate(
total_posts=Count('id'),
avg_views=Avg('view_count'),
max_views=Max('view_count')
)
# {'total_posts': 150, 'avg_views': 245.6, 'max_views': 1500}
annotate(**kwargs) -> QuerySet
Add annotations to each result.
1
2
3
authors = await User.annotate(
post_count=Count('posts')
).filter(post_count__gt=5)
update(**kwargs) -> int
Update matching records.
Returns:
int
- Number of updated records
1
updated_count = await User.filter(is_active=False).update(is_active=True)
delete() -> int
Delete matching records.
Returns:
int
- Number of deleted records
1
deleted_count = await Post.filter(published=False).delete()
Query Filters
Lookup Types
Exact Match
1
2
User.filter(name='John') # name = 'John'
User.filter(age=25) # age = 25
Case Sensitivity
1
2
User.filter(name__iexact='john') # Case-insensitive exact match
User.filter(email__icontains='GMAIL') # Case-insensitive contains
String Operations
1
2
3
4
User.filter(name__contains='Jo') # name LIKE '%Jo%'
User.filter(name__startswith='J') # name LIKE 'J%'
User.filter(name__endswith='son') # name LIKE '%son'
User.filter(email__endswith='@gmail.com')
Null Checks
1
2
User.filter(bio__isnull=True) # bio IS NULL
User.filter(bio__isnull=False) # bio IS NOT NULL
Numeric Comparisons
1
2
3
4
5
User.filter(age__gt=18) # age > 18
User.filter(age__gte=18) # age >= 18
User.filter(age__lt=65) # age < 65
User.filter(age__lte=65) # age <= 65
User.filter(score__range=(80, 100)) # score BETWEEN 80 AND 100
List Operations
1
2
User.filter(id__in=[1, 2, 3, 4]) # id IN (1, 2, 3, 4)
User.filter(status__in=['active', 'premium'])
Date/Time Filters
1
2
3
4
5
Post.filter(created_at__date=date.today()) # Extract date part
Post.filter(created_at__year=2024) # Extract year
Post.filter(created_at__month=1) # Extract month
Post.filter(created_at__day=15) # Extract day
Post.filter(created_at__week_day=1) # Monday = 1
Relationship Filters
1
2
3
Post.filter(author__name='John') # JOIN with author
Post.filter(author__age__gte=18) # Chain through relations
Post.filter(tags__name__in=['python', 'web']) # Many-to-many filter
Model Meta Options
Meta
Class
Configure model behavior with the Meta
inner class.
1
2
3
4
5
6
7
8
9
10
class User(Model):
name = Field(FieldType.STRING, max_length=100)
email = Field(FieldType.EMAIL)
class Meta:
table_name = 'users'
ordering = ['-created_at']
indexes = ['email', 'created_at']
unique_together = [('name', 'email')]
abstract = False
Options
table_name: str
Custom database table name.
1
2
class Meta:
table_name = 'custom_users'
ordering: List[str]
Default ordering for queries.
1
2
class Meta:
ordering = ['-created_at', 'name'] # - for descending
indexes: List[str|List[str]]
Database indexes to create.
1
2
3
4
5
6
class Meta:
indexes = [
'email', # Single field index
['user_id', 'created_at'], # Composite index
'status'
]
unique_together: List[List[str]]
Unique constraints across multiple fields.
1
2
3
4
5
class Meta:
unique_together = [
['user_id', 'post_id'], # Unique combination
['slug', 'category']
]
abstract: bool
Whether model is abstract (no database table).
1
2
3
4
5
6
7
8
9
class BaseModel(Model):
created_at = Field(FieldType.DATETIME, auto_now_add=True)
updated_at = Field(FieldType.DATETIME, auto_now=True)
class Meta:
abstract = True # No table created
class User(BaseModel): # Inherits created_at, updated_at
name = Field(FieldType.STRING, max_length=100)
Model Validation
Field Validation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def validate_adult_age(value):
if value < 18:
raise ValueError("Must be 18 or older")
return value
class User(Model):
age = Field(FieldType.INTEGER, validators=[validate_adult_age])
email = Field(FieldType.EMAIL) # Built-in email validation
def clean_email(self):
"""Field-specific validation method"""
email = self.email.lower().strip()
if User.filter(email=email).exclude(id=self.id).exists():
raise ValueError("Email already exists")
return email
def clean(self):
"""Model-wide validation"""
if self.age < 13 and not self.parent_consent:
raise ValueError("Users under 13 require parent consent")
Custom Validation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pyframe.validation import ValidationError
class CustomValidator:
def __init__(self, min_length=8):
self.min_length = min_length
def __call__(self, value):
if len(value) < self.min_length:
raise ValidationError(f"Must be at least {self.min_length} characters")
if not any(c.isupper() for c in value):
raise ValidationError("Must contain uppercase letter")
return value
class User(Model):
password = Field(FieldType.STRING, validators=[CustomValidator(10)])
Database Operations
Raw SQL
1
2
3
4
5
6
7
8
9
10
11
# Raw queries when ORM isn't sufficient
users = await User.raw(
"SELECT * FROM users WHERE age > %s AND created_at > %s",
[18, datetime.now() - timedelta(days=30)]
)
# Raw SQL with parameters
result = await User.execute_sql(
"UPDATE users SET last_login = %s WHERE id = %s",
[datetime.now(), user_id]
)
Transactions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from pyframe.database import transaction
async with transaction():
user = await User.create(name='John', email='john@example.com')
profile = await UserProfile.create(user=user, bio='Developer')
await send_welcome_email(user.email)
# All operations committed together, or all rolled back on error
# Decorator syntax
@transaction
async def create_user_with_profile(name, email, bio):
user = await User.create(name=name, email=email)
await UserProfile.create(user=user, bio=bio)
return user
This completes the Model API reference. The PyFrame ORM provides a powerful yet intuitive interface for database operations! 🗄️