Chapter 4: Django Models
4.3 Django Model Methods and Properties
In Django models, in addition to defining fields, we can also add methods and properties to enhance their functionality. These methods and properties can help us implement business logic, data validation, calculated properties, and more.
__str__ Method
__str__ provides a readable string representation for model instances.
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.titlePurpose:
- Display meaningful object names in Django admin
- Provide readable information when debugging in Shell
- Used when displaying objects in templates
Custom Methods
We can add custom methods to models to encapsulate business logic, making the code clearer and more maintainable.
Basic Custom Methods
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def is_recent(self):
"""Check if the article was recently published (within 24 hours)"""
from django.utils import timezone
return (timezone.now() - self.created_at).total_seconds() < 86400
def publish(self):
"""Publish the article"""
self.published = True
self.save()
def unpublish(self):
"""Unpublish the article"""
self.published = False
self.save()
def get_excerpt(self, length=100):
"""Get article excerpt"""
if len(self.content) <= length:
return self.content
return self.content[:length] + '...'Usage Examples
# Create article instance
article = Article.objects.create(
title="Django Model Methods Explained",
content="This is a detailed tutorial about Django model methods..."
)
# Call custom methods
print(article.is_recent()) # Check if recently published
print(article.get_excerpt(50)) # Get 50-character excerpt
article.publish() # Publish articleProperty Decorator
Using the @property decorator, we can create read-only calculated properties. These properties are not stored in the database but are dynamically calculated when accessed.
Basic Properties
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
word_count = models.PositiveIntegerField(default=0)
@property
def reading_time(self):
"""Estimate reading time (assuming 200 words per minute)"""
if self.word_count == 0:
return "Unknown"
minutes = self.word_count // 200
if minutes == 0:
return "Less than 1 minute"
return f"{minutes} minutes"
@property
def is_popular(self):
"""Determine if popular based on certain conditions"""
# More complex logic can be added here
return self.word_count > 1000Properties with Setters
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
@property
def formatted_title(self):
"""Get formatted title"""
return self.title.upper()
@formatted_title.setter
def formatted_title(self, value):
"""Set title (automatically convert to lowercase)"""
self.title = value.lower()Usage Examples
article = Article(title="Hello World", content="This is some content", word_count=450)
print(article.reading_time) # Output: 2 minutes
print(article.is_popular) # Output: False
article.formatted_title = "NEW ARTICLE"
print(article.title) # Output: new articleMeta Class Options
The Meta class is used to define model metadata. These options do not create database fields but affect the model's behavior.
Common Meta Options
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
# Database table name
db_table = 'blog_articles'
# Ordering rules
ordering = ['-created_at']
# Singular and plural names
verbose_name = 'Article'
verbose_name_plural = 'Articles'
# Permission settings
permissions = [
('can_publish', 'Can publish articles'),
('can_edit_all', 'Can edit all articles'),
]
# Unique constraints
constraints = [
models.UniqueConstraint(
fields=['title', 'created_at'],
name='unique_title_per_day'
)
]
# Indexes
indexes = [
models.Index(fields=['title']),
models.Index(fields=['created_at', 'published']),
]Meta Options Explained
- db_table: Specify database table name
- ordering: Default ordering rules
- verbose_name: Singular name (displayed in admin)
- verbose_name_plural: Plural name
- permissions: Additional permissions
- constraints: Database constraints
- indexes: Database indexes
- abstract: Whether it's an abstract base class
- proxy: Whether it's a proxy model
Complete Example
from django.db import models
from django.utils import timezone
from django.urls import reverse
class BlogPost(models.Model):
title = models.CharField(max_length=200, verbose_name="Title")
slug = models.SlugField(unique=True, verbose_name="URL Identifier")
content = models.TextField(verbose_name="Content")
author = models.ForeignKey('auth.User', on_delete=models.CASCADE, verbose_name="Author")
published = models.BooleanField(default=False, verbose_name="Published")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Created Time")
updated_at = models.DateTimeField(auto_now=True, verbose_name="Updated Time")
def __str__(self):
return self.title
@property
def reading_time(self):
"""Calculate reading time"""
word_count = len(self.content.split())
minutes = max(1, word_count // 200) # At least 1 minute
return f"{minutes} minutes"
@property
def is_recent(self):
"""Whether recently updated (within 7 days)"""
return (timezone.now() - self.updated_at).days < 7
def get_absolute_url(self):
"""Get absolute URL of the article"""
return reverse('blog:post_detail', kwargs={'slug': self.slug})
def publish(self):
"""Publish article"""
self.published = True
self.save()
def unpublish(self):
"""Unpublish"""
self.published = False
self.save()
class Meta:
verbose_name = "Blog Post"
verbose_name_plural = "Blog Posts"
ordering = ['-created_at']
indexes = [
models.Index(fields=['slug']),
models.Index(fields=['published', 'created_at']),
]
constraints = [
models.UniqueConstraint(fields=['slug'], name='unique_slug')
]Best Practices
- Keep methods simple: Each method should be responsible for one clear function
- Use property decorator: For calculated properties, use
@propertyinstead of methods - Use Meta appropriately: Use the Meta class to organize model-level configurations
- Consider performance: Avoid expensive database queries in methods
- Use caching: For properties with high computational cost, consider using caching
from django.core.cache import cache
class Article(models.Model):
# ... field definitions ...
@property
def popular_tags(self):
"""Get popular tags (with caching)"""
cache_key = f'article_{self.id}_popular_tags'
tags = cache.get(cache_key)
if tags is None:
# Expensive calculation or query
tags = self.tags.all()[:5]
cache.set(cache_key, tags, timeout=300) # Cache for 5 minutes
return tagsSummary
Model methods and properties are powerful features of Django ORM that allow us to:
- ✅ Add business logic to model instances
- ✅ Create calculated properties
- ✅ Define model-level configurations
- ✅ Improve code readability and maintainability
By using these features appropriately, we can create more robust and flexible Django applications.
Next
We will learn about database operations, including database configuration, migration system, and QuerySet usage.
5.1 Database Operations Basics →