Search⌘ K
AI Features

Leveraging Database Views and Proxy Models

Explore how to enhance Django admin by using unmanaged database views for efficient complex reporting and proxy models to create multiple admin interfaces for the same data. Learn to safely register read-only views, distinguish proxy from abstract models, and build alternate presentations to meet diverse administrative needs.

As your application matures, the administration panel often needs to evolve beyond basic data entry. Stakeholders may request complex reporting dashboards that aggregate data across millions of rows. They may also need specialized user interfaces that present the exact same database records in completely different configurations. We fulfill these advanced requirements using unmanaged database views and Django proxy models.

Exposing database views with unmanaged models

A standard Django model maps to a standard database table. However, relational databases also support views. Views are virtual tables generated dynamically by an underlying SQL query. Database views are highly efficient for complex aggregations and reporting because they do not duplicate data.

To expose a pre-existing SQL view in the Django admin, we map a model to it using the managed = False constraint inside the model’s Meta class. This tells the migration engine of Django to completely ignore this model when creating or altering database tables.

Python
# File: sample_app/models.py
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=200)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Authors"
def __str__(self):
return f"{self.name}"
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
ref_author = models.ForeignKey(Author, on_delete=models.CASCADE)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Questions"
def __str__(self):
return f"[{self.ref_author.name}] : {self.question_text}"
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
created_date = models.DateTimeField(auto_now_add=True)
updated_date = models.DateTimeField(auto_now=True)
class Meta:
verbose_name_plural = "Choices"
def __str__(self):
return f"{self.question.question_text} : {self.choice_text}"
class QuestionSummary(models.Model):
month = models.DateField(primary_key=True)
total_questions = models.IntegerField()
class Meta:
managed = False
db_table = 'reporting_question_summary'
verbose_name_plural = 'Question Summaries'
  • Lines 43–44: Define the fields that match the output columns of our SQL view. Django requires a primary key, even for unmanaged views. ...