Django中如何引用继承模型?基于GET参数筛选子类模型遇阻
问题描述
我想在Django中根据继承的模型类型筛选产品,尝试用返回模型名称的model_name属性实现,但筛选未生效。需求是通过GET请求传入的分类值,在get_queryset()方法里过滤出对应类型的产品。
现有代码
models.py
class Product(PolymorphicModel): title = models.CharField(max_length=100, blank=True) image = models.ImageField(upload_to='product', default=None) quantity = models.IntegerField(null=False) is_available = models.BooleanField(default=True, null=False) price = models.IntegerField(null=False, blank=False, default=15) popularity = models.IntegerField(default=0) def __str__(self): return str(self.title) def get_absolute_url(self): return reverse("ProductDetail", args=[str(self.pk)]) @property def model_name(self): return self._meta.model_name class CD(Product): GENRE_CHOICES = ( ('Disco', 'Disco'), ('Electronic music', 'Electronic music'), ('Rap', 'Rap'), ('Reggae', 'Reggae'), ('Rock', 'Rock'), ('Pop', 'Pop'), ) band = models.CharField(max_length=100, null=False, blank=False) tracklist = models.TextField(max_length=500, null=False, blank=False) genre = models.CharField(max_length=100, choices=GENRE_CHOICES, null=False, blank=False) class Book(Product): GENRE_CHOICES = ( ('Biography', 'Biography'), ('Criminal', 'Criminal'), ('Fantasy', 'Fantasy'), ('Historical Novel', 'Historical Novel'), ('Horror', 'Horror'), ('Romance', 'Romance'), ('Sci-Fi', 'Sci-Fi'), ) author = models.CharField(max_length=100, null=False, blank=False) isbn = models.CharField(max_length=100, null=False, blank=False, unique=True) genre = models.CharField(max_length=100, choices=GENRE_CHOICES, null=False, blank=False) class Film(Product): GENRE_CHOICES = ( ('Adventure', 'Adventure'), ('Animated', 'Animated'), ('Comedy', 'Comedy'), ('Horror', 'Horror'), ('Thriller', 'Thriller'), ('Romance', 'Romance'), ) director = models.CharField(max_length=100, null=False, blank=False) duration = models.IntegerField(null=False, blank=False) genre = models.CharField(max_length=100, choices=GENRE_CHOICES, null=False, blank=False)
views.py
class Statistics(ListView): model = Product template_name = 'orders/statistics.html' def get_queryset(self): qs = super(Statistics, self).get_queryset() if self.request.GET: qs = qs.filter(model_name=self.request.GET['category']) return qs def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) data['form'] = CategoryForm return data
forms.py
from django import forms class CategoryForm(forms.Form): CATEGORY_CHOICES = ( ('book', 'Book'), ('cd', 'CD'), ('film', 'Film'), ) category = forms.ChoiceField(choices=CATEGORY_CHOICES)
模板文件
{% extends 'base.html' %} {% load static %} {% block content %} {% for item in object_list %} <form method="get"> {{ form }} <input type="submit" value="Search"> </form> <table> <tr> <th>Title</th> <th>Category</th> <th>Genre</th> <th>Popularity</th> </tr> <tr> <td>{{ item.title }}</td> <td>{{ item.model_name}}</td> <td>{{ item.genre }}</td> <td>{{item.popularity}}</td> </tr> </table> {% endfor %} {% endblock %}
问题原因
- 使用
filter(model_name=...)是错误的:model_name是你定义的实例属性,并非数据库字段,Django ORM无法直接用它构建数据库查询语句。 - 模板中表单放在
{% for %}循环内部,会导致每个产品都渲染一次表单,属于冗余操作。
解决方案
1. 修改视图的筛选逻辑
由于使用了PolymorphicModel,它自带instance_of()方法可直接筛选指定子类的实例。我们可以建立分类值到模型类的映射,再用该方法过滤:
修改views.py的get_queryset方法:
from .models import Book, CD, Film # 导入子类模型 class Statistics(ListView): model = Product template_name = 'orders/statistics.html' def get_queryset(self): qs = super().get_queryset() category = self.request.GET.get('category') # 建立分类值与对应模型的映射 category_model_map = { 'book': Book, 'cd': CD, 'film': Film, } if category in category_model_map: # 筛选对应子类的实例 qs = qs.instance_of(category_model_map[category]) return qs def get_context_data(self, **kwargs): data = super().get_context_data(**kwargs) data['form'] = CategoryForm return data
2. 修复模板的表单位置
将表单移到循环外面,避免重复渲染:
{% extends 'base.html' %} {% load static %} {% block content %} <!-- 表单移至循环外部 --> <form method="get"> {{ form }} <input type="submit" value="Search"> </form> {% for item in object_list %} <table> <tr> <th>Title</th> <th>Category</th> <th>Genre</th> <th>Popularity</th> </tr> <tr> <td>{{ item.title }}</td> <td>{{ item.model_name}}</td> <td>{{ item.genre }}</td> <td>{{item.popularity}}</td> </tr> </table> {% empty %} <p>暂无相关产品</p> {% endfor %} {% endblock %}
额外优化方案
如果不想依赖PolymorphicModel的instance_of方法,也可以直接通过子类模型的objects.all()获取数据:
def get_queryset(self): category = self.request.GET.get('category') category_model_map = { 'book': Book, 'cd': CD, 'film': Film, } if category in category_model_map: return category_model_map[category].objects.all() # 默认返回所有产品 return Product.objects.all()
内容的提问来源于stack exchange,提问作者Tylzan
相关产品推荐
相关产品推荐

