简体   繁体   English

如何过滤Django模型的查询集

[英]How to filter the queryset of a django modelformset manytomany memeber

I can't use the proper code but I found the pizza/toppings question and it's close so I'm modifying it to ask my question. 我无法使用正确的代码,但是我发现了披萨/浇头的问题,并且问题很近,因此我正在对其进行修改以询问我的问题。 Django ModelForm for Many-to-Many fields Django ModelForm用于多对多字段

We have Pizza and Topping which are great. 我们有很棒的披萨和浇头。 And let's say that we operate a chain of stores and not all stores have all ingredients. 假设我们经营连锁商店,但并非所有商店都包含所有成分。 Which means we need a Store class that has a location and a manytomany for toppings. 这意味着我们需要一个Store类,该类具有一个位置,并且浇头的位置很多。 And then let's roll an Order which references a store and has a manytomany on Pizza since you often order more than one. 然后让我们下一个订单,该订单引用一家商店并且在Pizza上有很多内容,因为您经常订购多个。

models.py models.py

from django import models

class Topping(models.Model):
    name = models.TextField()

class Pizza(models.Model):
    size = models.TextField()
    toppings = models.ManyToManyField(Topping)

class Restaraunt(models.Model):
    name = models.TextField()
    toppings = models.ManyToManyField(Topping)


class Order(models.Model):
    customer = models.ForeignKey('User')
    location = models.ForeignKey(Restaraunt)
    pizzas = models.ManyToManyField(Pizza)

forms.py 表格

from django import forms
from django.forms import ModelForm
from models import *

class PizzaForm(ModelForm):

    class Meta:
        model = Pizza

    toppings = forms.ModelMultipleChoiceField(
        widget=forms.CheckboxSelectMultiple(),
        queryset=Topping.objects.all()
    )

views.py views.py

from django.shortcuts import render, redirect, get_object_or_404
from django.forms.models import modelformset_factory
from django.forms import CheckboxSelectMultiple

from models import *
from forms import *


def order_pizzas(request, order_id):
    order_id = int(order_id)
    order = get_object_or_404(Order, id=order_id)

    restaraunt = order.restaraunt

    PizzaFormSet = modelformset_factory(Pizza, form=PizzaForm, extra=1)

    pizza_form = PizzaFormSet(request.POST or None)

    return render(request, "place_order.html", {
        'restaraunt': restaraunt,
        'pizza_form': pizza_form,
    })

Please don't beat me up too much if those don't actually run. 如果这些没有实际运行,请不要打我太多。 Like I said I can't post the real code here for various reasons, partially because it's just massive. 就像我说的那样,由于种种原因我不能在此处发布真实的代码,部分原因是它太庞大了。

For the sake of this example assume that the person has already located the restaraunt closest to their home and started the ordering process. 为了本示例的目的,假定该人员已经将餐厅放置在离他们家最近的位置并开始订购过程。

I have tried passing in a widget to the modelformset_factory with the proper name and options but that didn't propagate. 我尝试过使用正确的名称和选项将小部件传递给modelformset_factory,但这种方式并未传播。

widgets = {'toppings': 
    CheckboxSelectMultiple(
        choices=[t.id for t in restaraunt.toppings.all().order_by('-id')]
    )}

I also tried extending the BaseModelFormSet to pass in extra data and try and get that into the PizzaForm but I got stuck. 我还尝试扩展BaseModelFormSet以传递额外的数据,并尝试将其输入到PizzaForm中,但是我被卡住了。

Basically I can tell that I need to somehow propagate information from the view to the formset to the form so that the queryset on the form can be initialized properly. 基本上,我可以告诉我需要以某种方式将信息从视图传播到表单集再传播到表单,以便可以正确初始化表单上的queryset。 I just can't figure out precisely how to do that. 我只是不知道该怎么做。

So this is the closest I've found to an answer but I can't seem to figure out how to make that work for sure: Django filter ModelFormSet field choices... different from limiting the Formset's queryset 因此,这是我找到的最接近答案的方法,但我似乎无法确定如何使该方法有效: Django过滤器ModelFormSet字段选择...与限制Formset的queryset不同

In response to people who are going to badger me for posting a non-working example 回应有人要badge我张贴不工作的例子

The point is that I'm not trying to ask for someone to post the exact 10 lines of code that'll completely solve my problem, but rather that I'm showing where my knowledge is lacking. 关键是,我不是要别人张贴完全能解决我的问题的10行代码,而是要显示我所缺乏的知识。 I know that I generate a PizzaFormSet and that eventually somewhere in the bowels of that code it uses the PizzaForm I specify. 我知道我生成了PizzaFormSet,并且最终在该代码的某处使用了我指定的PizzaForm。 But I don't have any idea how to successfully pass information from the PizzaFormSet to the PizzaForm. 但是我不知道如何成功地将信息从PizzaFormSet传递到PizzaForm。

Basically I'm willing to give away a bounty for a suggestion as to what part of this puzzle I'm missing. 基本上,我愿意就我遗漏的难题的哪一部分提出建议。

Where the problem is 问题出在哪里

I have defined a form in forms.py (PizzaForm) which needs to get a situationally-dependent queryset for the Topping. 我已经在forms.py(PizzaForm)中定义了一个表单,该表单需要为Topping获取一个与情境相关的查询集。 The view order_pizzas determines which restaurant will make and deliver the pizzas and the toppings available at that restaurant might be different than at other restaurants. view order_pizzas视图确定哪个餐厅将制作和交付比萨饼,并且该餐厅可用的浇头可能与其他餐厅不同。

I don't know how to propagate that information from the view to the form, normally you just subclass the form and add some extra init kwargs to do whatever you want. 我不知道如何将这些信息从视图传播到表单,通常情况下,您只是将表单子类化,并添加一些额外的初始化参数即可执行所需的任何操作。

But in this case I'm using a formset rather than a single form. 但是在这种情况下,我使用的是表单集而不是单个表单。 That means I have to find (or make) some channel to pass the restaurant information and/or the specific queryset through from the view to the formset to the form. 这意味着我必须找到(或建立)一些渠道,以将餐厅信息和/或特定的查询集从视图传递到表单集再传递到表单。 I think that's my main point of confusion and/or ignorance. 我认为这是我的困惑和/或无知的要点。

Here's a solution. 这是一个解决方案。 There may be a better one, but this should work. 可能会有更好的选择,但这应该可行。 Loop through all the forms in the formset, and change the choices variable on the toppings field. 循环浏览表单集中的所有表单,然后在toppings字段中更改choices变量。 Like this: 像这样:

pizza_form = PizzaFormSet(request.POST or None)

choices = [(t.pk, unicode(t)) for t in restaraunt.toppings.all().order_by('-id')]

for form in pizza_form:
    form.fields['toppings'].choices = choices

You could also override the BaseModelFormset and override the _contruct_forms method, passing in the restaraunt object to the form's __init__ , then change the topping's choices there. 您还可以覆盖BaseModelFormset_contruct_forms方法,将restaraunt对象传递给表单的__init__ ,然后在此处更改顶部的选择。 But I think the above solution is the quickest and simplest. 但我认为上述解决方案是最快,最简单的。 It just introduces an extra loop. 它只是引入了一个额外的循环。

You can have a factory that makes PizzaForms: 您可以拥有一家制造PizzaForms的工厂:

def pizzaform_factory(restaurant):
    class PizzaForm(ModelForm):

        class Meta:
            model = Pizza

        toppings = forms.ModelMultipleChoiceField(
            widget=forms.CheckboxSelectMultiple(),
            queryset=restaurant.toppings.all()
        )
    return PizzaForm

And use it in your view: 并在您的视图中使用它:

PizzaForm = pizzaform_factory(restaurant)
PizzaFormSet = modelformset_factory(Pizza, form=PizzaForm, extra=1)

Perhaps a constraint on your models to make sure you can't create impossible orders would be nice. 也许对您的模型进行约束以确保您无法创建不可能的订单会很好。 If you don't use the pizzaform_factory (ie. using the admin, a shell script or an API call) you can still end up with unfulfillable orders. 如果您不使用pizzaform_factory (即使用管理员,shell脚本或API调用),您仍然会遇到无法完成的订单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM