InlineFormSet with queryset of different model

旧巷老猫 提交于 2021-02-07 10:24:27

问题


What we're trying to do is populate a list of inline forms with initial values using some queryset of a different model. We have products, metrics (some category or type or rating), and a rating, which stores the actual rating and ties metrics to products.

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)

The end result we're going for is a list of all possible ratings for a Product on the Product admin page. If we have 20 Metrics in our database, when we go to the Product page we want to see 20 forms for Ratings on the page, each one tied to a different Metric. We can't use a queryset based on Ratings to populate the page, because the Rating for a particular Product/Metric combination might not yet exist.

We've been looking at all the forms and formset code in Django, and are hoping to come up with a solution as simple as this:

http://www.thenestedfloat.com/articles/limiting-inline-admin-objects-in-django

He just overrides something in BaseInlineFormSet and gives it to the inline. Maybe we can just make something like

class RatingInlineFormset(BaseInlineFormset):

With some overrides. Any ideas?


回答1:


Are you looking for an admin or front-end solution? The admin way is the following, you could reverse engineer it to get a similar front-end solution:

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



回答2:


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

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



来源:https://stackoverflow.com/questions/1269052/inlineformset-with-queryset-of-different-model

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!