django形式:以单一形式编辑多组相关对象 [英] django forms: editing multiple sets of related objects in a single form

查看:145
本文介绍了django形式:以单一形式编辑多组相关对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试做一些非常常见的事情:以单一形式添加/编辑一堆相关模型。例如:

I'm trying to do something that should be very common: add/edit a bunch of related models in a single form. For example:

Visitor Details:
Select destinations and activities:
    Miami  []   -  swimming [], clubbing [], sunbathing[]
    Cancun []   -  swimming [], clubbing [], sunbathing[]

我的模型是访问者,目的地和活动,访问者通过一个中间模型VisitorDestination拥有一个ManyToMany字段到Destination,该访问者具有目的地上要完成的活动的详细信息(本身就是一个ManyToMany字段活动)。

My models are Visitor, Destination and Activity, with Visitor having a ManyToMany field into Destination through an intermediary model, VisitorDestination, which has the details of the activities to be done on the destination (in itself a ManyToMany field into Activity).

Visitor ---->(M2M though VisitorDestination) -------------> Destination
                                            |
                       activities            ---->(M2M)---> Activity  

请注意,我不想输入新的目标/活动值,只是从db中可用的那些选择(但是这是M2M字段的完全合法用途?)

Note that I don't want to enter new destination / activity values, just choose from those available in the db (but that's a perfectly legit use of M2M fields right?)

对我来说,这看起来像一个非常常见的情况(与另外一个FK或M2M领域的其他细节有很多关系),这看起来像是最明智的建模,但如果我错了,请更正我。

To me this looks like an extremely common situation (a many to many relation with additional details which are a FK or M2M field into some other model), and this looks like the most sensible modelling, but please correct me if I'm wrong.

我花了几天时间搜索Django docs / SO / googling,但无法解决如何处理这个问题。我尝试了几种方法:

I've spent a few days searching Django docs / SO / googling but haven't been able to work out how to deal with this. I tried several approaches:


  1. 访客的自定义模型表单,其中为目标和活动添加了多个选择字段。如果目的地和活动可以单独选择,但这里它们相关,即我想为每个目的地

使用 inlineformset_factory 生成一组目标/活动表单,其中 inlineformset_factory(Destination,Visitor) 。由于访问者与目的地有M2M关系,而不是FK。

Using inlineformset_factory to generate the set of destination / activities forms, with inlineformset_factory(Destination, Visitor). This breaks, because Visitor has a M2M relation to Destination, rather than a FK.

使用 formset_factory定制一个简单的表单集, code>,例如 DestinationActivityFormSet = formset_factory(DestinationActivityForm,extra = 2)。但是如何设计 DestinationActivityForm ?我没有足够的探索,但它看起来并不很有希望:我不想输入目的地和活动列表,我想要一个复选框列表,标签设置为目标/活动我想要的选择,但 formset_factory 将返回具有相同标签的表单列表。

Customizing a plain formset, using formset_factory, eg DestinationActivityFormSet = formset_factory(DestinationActivityForm, extra=2). But how to design DestinationActivityForm? I haven't explored this enough, but it doesn't look very promising: I don't want to type in the destination and a list of activities, I want a list of checkboxes with the labels set to the destination / activities I want to select, but the formset_factory would return a list of forms with identical labels.

我是一个与django完整的新手,所以也许解决方案是显而易见的,但我发现这方面的文档很弱 - 如果有人有一些指针用于表单/表单的例子,那将是还有帮助

I'm a complete newbie with django so maybe the solution is obvious, but I find that the documentation in this area is very weak - if anyone has some pointers to examples of use for forms / formsets that would be also helpful

谢谢!

推荐答案

最后我选择了在同一视图中处理多个表单,访问者模型表单的访问者详细信息,然后列出每个目的地的自定义表单。

In the end I opted for processing multiple forms within the same view, a Visitor model form for the visitor details, then a list of custom forms for each of the destinations.

同时处理多个表单视图原来足够简单(至少在这种情况下,没有跨场验证问题)。

Processing multiple forms in the same view turned out to be simple enough (at least in this case, where there were no cross-field validation issues).

我仍然感到惊讶的是,没有内置的支持与中介模型的许多关系,并在网络环顾四周,我发现没有直接引用它。我会发布代码,以防它有助于任何人。

I'm still surprised there is no built-in support for many to many relationships with an intermediary model, and looking around in the web I found no direct reference to it. I'll post the code in case it helps anyone.

首先自定义表单:

class VisitorForm(ModelForm):
    class Meta:
      model = Visitor
      exclude = ['destinations']

class VisitorDestinationForm(Form):
    visited = forms.BooleanField(required=False)
    activities = forms.MultipleChoiceField(choices = [(obj.pk, obj.name) for obj in Activity.objects.all()], required=False, 
                                                      widget = CheckboxSelectMultipleInline(attrs={'style' : 'display:inline'}))

    def __init__(self, visitor, destination, visited,  *args, **kwargs):
        super(VisitorDestinationForm, self).__init__(*args, **kwargs)
        self.destination = destination
        self.fields['visited'].initial = visited
        self.fields['visited'].label= destination.destination

        # load initial choices for activities
        activities_initial = []
        try:
            visitorDestination_entry = VisitorDestination.objects.get(visitor=visitor, destination=destination)
            activities = visitorDestination_entry.activities.all()
            for activity in Activity.objects.all():
                if activity in activities: 
                    activities_initial.append(activity.pk)
        except VisitorDestination.DoesNotExist:
            pass
        self.fields['activities'].initial = activities_initial

我通过传递一个访问者定制每个表单目标对象(以及为了方便计算外部计算的访问标志)

I customize each form by passing a Visitor and Destination objects (and a 'visited' flag which is calculated outside for convenience)

我使用布尔值字段以允许用户选择每个目的地。该字段被称为访问,但是我将标签设置到目的地,以便它很好地显示。

I use a boolean field to allow the user to select each destination. The field is called 'visited', however I set the label to the destination so it gets nicely displayed.

活动由通常的MultipleChoiceField处理(我使用我自定义的窗口小部件来获取在桌子上显示的复选框,非常简单,但如果有人需要则可以发布)

The activities get handled by the usual MultipleChoiceField (I used I customized widget to get the checkboxes to display on a table, pretty simple but can post it if somebody needs that)

然后查看代码:

def edit_visitor(request, pk):
    visitor_obj = Visitor.objects.get(pk=pk)
    visitorDestinations = visitor_obj.destinations.all()
    if request.method == 'POST':
        visitorForm = VisitorForm(request.POST, instance=visitor_obj)

        # set up the visitor destination forms
        destinationForms = []
        for destination in Destination.objects.all():
            visited = destination in visitorDestinations
            destinationForms.append(VisitorDestinationForm(visitor_obj, destination, visited, request.POST, prefix=destination.destination))

        if visitorForm.is_valid() and all([form.is_valid() for form in destinationForms]):
            visitor_obj = visitorForm.save()
            # clear any existing entries,
            visitor_obj.destinations.clear()
            for form in destinationForms:
                if form.cleaned_data['visited']: 
                    visitorDestination_entry = VisitorDestination(visitor = visitor_obj, destination=form.destination)
                    visitorDestination_entry.save()
                    for activity_pk in form.cleaned_data['activities']: 
                        activity = Activity.objects.get(pk=activity_pk)
                        visitorDestination_entry.activities.add(activity)
                    print 'activities: %s' % visitorDestination_entry.activities.all()
                    visitorDestination_entry.save()

            success_url = reverse('visitor_detail', kwargs={'pk' : visitor_obj.pk})
            return HttpResponseRedirect(success_url)
    else:
        visitorForm = VisitorForm(instance=visitor_obj)
        # set up the visitor destination forms
        destinationForms = []
        for destination in Destination.objects.all():
            visited = destination in visitorDestinations
            destinationForms.append(VisitorDestinationForm(visitor_obj, destination, visited,  prefix=destination.destination))

    return render_to_response('testapp/edit_visitor.html', {'form': visitorForm, 'destinationForms' : destinationForms, 'visitor' : visitor_obj}, context_instance= RequestContext(request))

只需在列表中收集我的目标表单,并将此列表传递给我的模板,以便它可以迭代并显示它们。它运作良好,只要你不要忘了通过不同的前缀为每一个在构造

I simply collect my destination forms in a list and pass this list to my template, so that it can iterate over them and display them. It works well as long as you don't forget to pass a different prefix for each one in the constructor

我会离开的问题打开几天,以防一些人有一个更干净的方法。

I'll leave the question open for a few days in case some one has a cleaner method.

谢谢!

这篇关于django形式:以单一形式编辑多组相关对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆