Django:如何从QuerySet获取表名,实现DetailView正确跳转?
解决Django跨模型列表视图的类型判断与详情页跳转问题
一、获取对象的模型标识(CD/Book)
有两种常用方法可以在模板中区分不同模型的对象:
方法1:直接调用对象的类属性
在Django模板中,可通过object.__class__.__name__直接获取对象所属模型的类名(比如Book对象返回"Book",CD对象返回"CD"),结合模板标签实现条件判断:
{% block content %} <div class="row"> {% for object in object_list %} <div class="col-md-3"> <div class="card card-product-grid"> <img src="{{ object.image.url }}"> {% if object.__class__.__name__ == 'Book' %} <a href="{% url 'DetailBook' object.pk %}" class="title">{{ object.title }}</a> {% elif object.__class__.__name__ == 'CD' %} <a href="{% url 'DetailCD' object.pk %}" class="title">{{ object.title }}</a> {% elif object.__class__.__name__ == 'Film' %} <a href="{% url 'DetailFilm' object.pk %}" class="title">{{ object.title }}</a> {% endif %} </div> </div> {% endfor %} </div> {% endblock content %}
方法2:在模型中添加自定义属性
如果担心后续模型类名修改导致模板失效,可在每个模型中定义只读属性明确返回模型标识:
# models.py class Book(models.Model): # 原有字段定义 title = models.CharField(max_length=200) image = models.ImageField(upload_to='books/') @property def model_type(self): return 'Book' class CD(models.Model): # 原有字段定义 title = models.CharField(max_length=200) image = models.ImageField(upload_to='cds/') @property def model_type(self): return 'CD' class Film(models.Model): # 原有字段定义 title = models.CharField(max_length=200) image = models.ImageField(upload_to='films/') @property def model_type(self): return 'Film'
之后模板中用object.model_type判断,写法更直观:
{% if object.model_type == 'Book' %} <a href="{% url 'DetailBook' object.pk %}" class="title">{{ object.title }}</a> {% elif object.model_type == 'CD' %} <a href="{% url 'DetailCD' object.pk %}" class="title">{{ object.title }}</a> {% endif %}
二、优化ListView与DetailView的实现方案
当前通过chain合并多个QuerySet再排序的方式存在明显缺陷:
- 性能低下:将QuerySet转为列表会触发全量数据库查询,内存排序在数据量大时效率极差;
- 分页失效:ListView默认分页仅支持单个QuerySet,合并后的列表无法直接使用内置分页功能;
推荐以下两种优化方案:
方案1:使用抽象基类+多态查询
将三个模型抽象出公共基类,借助django-polymorphic库实现多态查询(需先安装:pip install django-polymorphic):
1.1 定义模型
# models.py from django.db import models from polymorphic.models import PolymorphicModel class Product(PolymorphicModel): # 公共字段 title = models.CharField(max_length=200) image = models.ImageField(upload_to='products/') class Book(Product): # 书籍特有字段 author = models.CharField(max_length=100) class CD(Product): # CD特有字段 artist = models.CharField(max_length=100) class Film(Product): # 电影特有字段 director = models.CharField(max_length=100)
1.2 简化ListView
直接查询基类Product即可获取所有子类对象,且支持数据库层面排序:
# views.py from django.views.generic import ListView from .models import Product class Home(ListView): template_name = 'home.html' queryset = Product.objects.all().order_by('title')
1.3 通用DetailView
借助多态特性,一个视图即可处理所有子类对象,自动匹配对应模板(需创建book_detail.html、cd_detail.html等模板):
# views.py from django.views.generic import DetailView from .models import Product class ProductDetailView(DetailView): model = Product
路由配置简化为:
# urls.py from django.urls import path from .views import Home, ProductDetailView urlpatterns = [ path('', Home.as_view(), name='Home'), path('product/<int:pk>/', ProductDetailView.as_view(), name='ProductDetail'), ]
模板中跳转无需判断类型,统一使用同一个URL:
<a href="{% url 'ProductDetail' object.pk %}" class="title">{{ object.title }}</a>
方案2:不依赖第三方库的通用视图实现
如果不想引入第三方库,可手动实现通用详情页视图:
# views.py from django.views.generic import DetailView from django.shortcuts import get_object_or_404 from .models import Book, CD, Film class ProductDetailView(DetailView): def get_object(self): pk = self.kwargs['pk'] # 依次尝试获取不同模型的对象 try: obj = Book.objects.get(pk=pk) self.template_name = 'book_detail.html' return obj except Book.DoesNotExist: pass try: obj = CD.objects.get(pk=pk) self.template_name = 'cd_detail.html' return obj except CD.DoesNotExist: pass obj = get_object_or_404(Film, pk=pk) self.template_name = 'film_detail.html' return obj
路由和模板跳转逻辑同方案1,同样可以实现统一跳转。
内容的提问来源于stack exchange,提问作者Tylzan
相关产品推荐
相关产品推荐

