且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

具有不同模型的queryset的InlineFormSet

更新时间:2023-12-02 15:10:04

我设法实现了类似的功能,如下所示:

I have managed to implement similar functionality a bit like this:

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,
)

条件必须在左联接中完成,而不是在WHERE中完成,否则您将缺少行.

The condition must be done in the LEFT JOIN, not the WHERE, otherwise you will have missing lines.