Skip to content

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.

python
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.title

Purpose:

  • 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

python
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

python
# 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 article

Property 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

python
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 > 1000

Properties with Setters

python
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

python
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 article

Meta 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

python
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

  1. db_table: Specify database table name
  2. ordering: Default ordering rules
  3. verbose_name: Singular name (displayed in admin)
  4. verbose_name_plural: Plural name
  5. permissions: Additional permissions
  6. constraints: Database constraints
  7. indexes: Database indexes
  8. abstract: Whether it's an abstract base class
  9. proxy: Whether it's a proxy model

Complete Example

python
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

  1. Keep methods simple: Each method should be responsible for one clear function
  2. Use property decorator: For calculated properties, use @property instead of methods
  3. Use Meta appropriately: Use the Meta class to organize model-level configurations
  4. Consider performance: Avoid expensive database queries in methods
  5. Use caching: For properties with high computational cost, consider using caching
python
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 tags

Summary

Model methods and properties are powerful features of Django ORM that allow us to:

  1. ✅ Add business logic to model instances
  2. ✅ Create calculated properties
  3. ✅ Define model-level configurations
  4. ✅ 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 →

Contents

Back to Course Outline

Released under the [BY-NC-ND License](https://creativecommons.org/licenses/by-nc-nd/4.0/deed.en).