Django:如何使用 ModelFormSet 和 FormWizard 过滤 ForeignKey 选择(例如使用 request.user)?

Posted

技术标签:

【中文标题】Django:如何使用 ModelFormSet 和 FormWizard 过滤 ForeignKey 选择(例如使用 request.user)?【英文标题】:Django: How to filter ForeignKey choices (e.g. with request.user) with ModelFormSet and FormWizard? 【发布时间】:2013-09-05 05:32:27 【问题描述】:

ModelForm 中有几个 ForeignKeys 我想在带有modelformset_factory 的表单向导中使用(不是 100% 确定表单集)我想知道如何限制下拉字段的选择,因为我需要动态地做。我想通过编写自己的 modelformset 工厂来尝试它,但在 *** 上我读到了其他方法,但不幸的是我不理解它们。

这就是我走了多远:

models.py

#...
class Attendee(models.Model):
    """Event specific attendee details."""
    # event is set by URL.
    event = models.ForeignKey(Event)
    attendee = models.ForeignKey(Person) # Contact details, should be limited to user
    accommodation = models.ForeignKey(Accommodation, blank=True) # *
    workshop = models.ForeignKey(Workshop, blank=True) # *
    volunteer = models.ForeignKey(Volunteer, blank=True) # *
    # *= should be limited to event but I think I will be able to handle that.
    #...

class AttendeeForm(forms.ModelForm):
    class Meta:
        model = Attendee

    def __init__(self, *args, **kwargs):
#    def __init__(self, user, *args, **kwargs): I tried this but I didn't get 
# it to work.
        super(AttendeeForm, self).__init__(*args, **kwargs)
#         self.fields['attendee'].queryset = Person.objects.filter(owner=user)
        self.fields['workshop'].required = True
        #...

# This is from https://***.com/a/4858044/2704544 but I don't understand it. ##
def form_setup(**kwargs):
    def make_form(data, prefix=None, initial=None):
        form = (data, prefix, initial)
        for k, v in kwargs.items():
            if k == 'some_list':
                form.fields['some_list'].choices = v # What does that mean?
            ...
        return form
    return make_form
#######################################################################################

(link 来源)

views.py

# This is from https://***.com/a/623198/2704544 but I don't understand it. ###
class Callback(object):
    def __init__(self, field_name, aff):
        self._field_name = field_name
        self._aff = aff # What is this aff?
    def cb(self, field, **kwargs):
        nf = field.formfield(**kwargs)
        if field.name == self._field_name:  # this is 'options' field
            nf.queryset = ServiceOption.objects.filter(affiliate=self._aff)
        return nf
#######################################################################################

reg_wiz_forms = (
    ('attendees', modelformset_factory(Attendee, form=AttendeeForm, exclude='event', 
# formfield_callback=Callback('option', affiliate).cb Just copied. Why can you call 
# cb without arguments?
)),)
class RegWizard(SessionWizardView):
    def get_form_instance(self, step):
        instance = None

        if step == '0':
            instance = Attendees.objects.filter(owner__owner=self.request.user)
        #...

(link 来源)

urls.py

#...
url(r'^register/$', views.RegWizard.as_view(views.reg_wiz_forms), name='register'),
#...

我还 read 关于 curry 和其他东西,并试图覆盖 get_initkwargsWizardView 的其他方法,但我找不到更多关于该主题的文档或提示。也许有人可以帮助我。

更新

它现在部分适用于curry。部分原因是它不适用于管理功能:

views.py

def manage_wizard(request, event):
    AttendeeFormSet = modelformset_factory(Attendee, form=AttendeeForm, exclude='event')
    AttendeeFormSet.form = staticmethod(curry(AttendeeForm, user=request.user))
    wiz = RegWizard
    wiz.event = event
    return wiz.as_view([('attendees', AttendeeFormSet)])

我得到一个 AttributeError: "'function' object has no attribute 'base_fields'" 这听起来像 here 的同一个问题。

但是当我覆盖 WizardViewget_form_list 并直接在 url.py 中调用它时,它可以工作:

reg_wiz_forms = ('attendees', modelformset_factory(Attendee, form=AttendeeForm, exclude='event'))

class RegWizard(SessionWizardView):
    def get_form_list(self):
        self.form_list['attendees'].form = staticmethod(curry(AttendeeForm, user=self.request.user))
        return super(RegWizard, self).get_form_list()

现在我想知道是否有不覆盖该方法的解决方案。

【问题讨论】:

【参考方案1】:

您只是希望您的模型上的参加者属性的外键查询集成为您的模型窗体上的过滤查询集。你在这里是正确的:

self.fields['attendee'].queryset = Person.objects.filter(owner=user)

这是假设 Person 类中存在属性“所有者”。

但是,这不起作用,因为您的用户参数在哪里或是什么?正如您所提到的,一种解决方案是使用表单 init 方法来包含正确的用户对象:

    form = staticmethod(curry(AttendeeForm, user=<the-user-obj>))

现在您在 init 方法中从 kwargs 中弹出您的用户 arg:

user = kwargs.pop('user')

现在您过滤的查询集将只显示您过滤的人。

def __init__(self, user, *args, **kwargs): I tried this but I didn't get 
it to work.

上面的行不能工作的原因有很多,主要是它永远不会从任何地方调用,你正在那里创建一个新函数,而不是覆盖现有的函数,这就是我们正在做的覆盖init 方法。

只是一些可能对设计有用的建议 - 这里有许多线程,它们都提供了许多不同的想法,使事情变得非常复杂。尝试将您的问题过滤到基本概念。在这里,它是数据问题的呈现,所以考虑从表单开始,这就是它的用途。 :-)

【讨论】:

现在我可以把它放在一起了。然后我尝试创建一个manage_wizard 函数来获取request 和事件slug 并创建ModelFormSets,但我认为我遇到了与here 完全相同的问题。您有什么建议不要覆盖WizardViewget_form_list(哪个有效)? 如果您还有其他问题,我建议您在一个新的 SO 问题中发布您的违规代码,以便我们大家看看,这个问题已得到解答。 其实是同一个问题。有趣的是,我首先以非常规的方式(压倒一切)尝试了它。但我不喜欢那样,所以我试图改变它失败了。有关更多详细信息,请参阅上面的更新。你还会发布一个新问题吗?如果,我仍然可以这样做,但它的标题相似。我也可以不接受你的回答。 我会说这个问题已经得到回答,基于你的问题“如何过滤外键......”的标题。我的回答回答了这个问题。您的覆盖方法绝对有效,是什么让您认为覆盖是“非常规”的? 100% 没问题。 好的。感谢您的耐心等待 :) 只是为了理解:您写的 def __init__(self, user, *args, **kwargs): 将不起作用。我有这个想法来自b-list。它是否适用于 Django 1.0 但不再适用?

以上是关于Django:如何使用 ModelFormSet 和 FormWizard 过滤 ForeignKey 选择(例如使用 request.user)?的主要内容,如果未能解决你的问题,请参考以下文章

Django 的 FormWizard 中的空 ModelFormset

如何在表单向导中使用 ModelFormSet

元类与 Django 中的 modelformset_factory 冲突

Django modelformset_factory UnboundLocalError

Django 使用 modelformset 组件批量修改表单数据

django modelformset 排除 ID 字段