使用不同模型的queryset的InlineFormSet
我们想做的是用其他模型的一些查询结果来填充一系列的内联表单,并给它们设置初始值。我们有产品、指标(某种类别、类型或评分)和评分,评分用来存储实际的评分,并将指标和产品关联起来。
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.IntegerField(max_length=6)
class Metric(models.Model):
name = models.CharField(max_length=80)
description = models.TextField()
class Rating(models.Model)
rating = models.IntegerField(max_length=3)
metric = models.ForeignKey(Metric)
product = models.ForeignKey(Product)
我们最终想要的结果是在产品管理页面上显示所有可能的产品评分。如果我们的数据库里有20个指标,当我们打开产品页面时,希望能看到20个评分的表单,每个表单都对应一个不同的指标。我们不能用基于评分的查询结果来填充页面,因为某个产品和指标的组合可能还没有评分。
我们一直在研究Django中的所有表单和表单集代码,希望能找到一个像这样简单的解决方案:
http://www.thenestedfloat.com/articles/limiting-inline-admin-objects-in-django
他只是重写了BaseInlineFormSet中的某些内容,然后把它应用到内联表单上。也许我们也可以做一些类似的事情:
class RatingInlineFormset(BaseInlineFormset):
加上一些重写的内容。有什么想法吗?
2 个回答
0
我已经成功实现了类似的功能,差不多是这样的:
from django.forms.models import BaseInlineFormSet
from django.forms.models import inlineformset_factory
class RawQueryAdapter(object):
"""
Implement some extra methods to make a RawQuery
compatible with FormSet, which is expecting a QuerySet
"""
ordered = True
def __init__(self, qs):
self.qs = qs
self.db = qs.db
def filter(self, *args, **kwargs):
return self
def __len__(self):
return len(list(self.qs))
def __getitem__(self, key):
return self.qs[key]
class BaseRatingFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
sql = """
SELECT r.id, %s as product_id, m.id as metric_id
FROM myapp_metric m
LEFT JOIN myapp_rating r ON m.id = r.metric_id
AND r.product_id = %s
"""
id = kwargs['instance'].id or 'NULL'
qs = RawQueryAdapter(Rating.objects.raw(sql % (id, id)))
super(BaseRatingFormSet, self).__init__(queryset=qs, *args, **kwargs)
def _construct_form(self, i, **kwargs):
pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
if self.data.get(pk_key) == '':
# Skip parent (BaseModelFormSet) implementation as it won't work
# with our injected raw data
if i < self.initial_form_count() and not kwargs.get('instance'):
kwargs['instance'] = self.get_queryset()[i]
return super(BaseModelFormSet, self)._construct_form(i, **kwargs)
return super(BaseRatingFormSet, self)._construct_form(i, **kwargs)
RatingFormSet = inlineformset_factory(
Product, Rating,
can_delete=False,
max_num=0,
formset=BaseRatingFormSet,
)
补充一下:这个条件要放在LEFT JOIN里,而不是WHERE里,不然会导致有些行缺失。
0
你是在寻找一个管理后台的解决方案,还是前端的解决方案呢?如果你想要管理后台的方式,可以按照下面的方法进行,你也可以通过逆向工程来得到一个类似的前端解决方案:
# admin.py
class RatingInline(admin.StackedInline):
model = Rating
class ProductAdmin(admin.ModelAdmin):
inlines = [
RatingInline
]
class MetricAdmin(admin.ModelAdmin):
pass
class RatingAdmin(admin.ModelAdmin):
pass
admin.site.register(Product, ProductAdmin)
admin.site.register(Metric, MetricAdmin)
admin.site.register(Rating, RatingAdmin)