【问题标题】:Many-to-Many Multiplechoice form with optional information带有可选信息的多对多选择题表格
【发布时间】:2017-04-24 16:48:17
【问题描述】:

在我的应用程序的先前版本中,我在 Account 和 Club 之间建立了多对多的关系。在我的 AccountForm 中,我使用“club = forms.MultipleChoiceField(widget=CheckboxSelectMultiple)”让用户能够从完整的俱乐部列表中进行选择。

▢ 足球

▢曲棍球

▢网球

▢游泳

但是,我现在需要包含一个可选字段,他们可以在其中包含他们的会员编号(如果有的话)。所以像

▢ 足球 ________

▢ 曲棍球 ________

▢ 网球 ________

▢ 游泳________

我意识到我必须使用through 模型,但现在我正在努力复制我以前的多选样式布局。

a) 我假设我需要使用内联表单集,但基于直通表,所以我需要以某种方式获取表单集工厂来为每个俱乐部创建表单。我不知道该怎么做。线索?

b) 包括一个复选框以反映该俱乐部的会员资格。所以大概是一个布尔字段,带有一个隐藏字段,指示俱乐部的 id,然后是一些自定义工作清理和保存功能。

这看起来是对的,还是有更简单的方法?

class Account(models.Model):
    name = models.CharField(max_length=20)
    address_street01 = models.CharField(max_length=50)
    address_pc = models.CharField(max_length=10)
    address_city = models.CharField(max_length=50)

class Club(models.Model):
    name = models.CharField(max_length=30, unique=True)

class Membership(models.Model):
    club = models.ForeignKey(Club)
    account = models.ForeignKey(Account)
    membership_ref = models.CharField(max_length=50, blank=True)

【问题讨论】:

    标签: django django-models django-forms django-templates


    【解决方案1】:

    我们将django-extra-views 中的ModelFormSetView 用于类似的用例。它不是由through 模型支持的,而是由具有多对一关系的表支持的,其中许多关系及其所有​​属性都显示为通过 ForeignKey 关联的主模型的详细视图的一部分。

    它也适用于through 模型,只需将through 模型作为ModelFormSetView 的模型属性即可。保存时甚至之前,通过get_extra_form_kwargs,您必须设置对定义 m2m 字段的主模型实例的引用。

    常规 django FormSets 的棘手之处在于(对我而言)它主要用于创建新对象,而我们只需要显示现有对象并修改它们。基本上,我们需要使用一次性保存的初始数据填充的重复表单。也可以删除它们。

    查看

    # You could additionally try to inherit from SingleObjectMixin
    # if you override the methods that refer to cls.model
    class ImportMatchView(ImportSessionMixin, ModelFormSetView):
        template_name = 'import_match.html'
        model = Entry  # this is your through model class
        form_class = EntryForm
        can_delete = True
    
        def get_success_url(self):
            return self.get_main_object().get_absolute_url()
    
        def get_factory_kwargs(self):
            kwargs = super().get_factory_kwargs()
            num = len(self.get_match_result())
            kwargs['extra'] = num  # this controls how many forms are generated
            kwargs['max_num'] = num  # no empty forms!
            return kwargs
    
        def get_initial(self):
            # override this if you have to previous m2m relations for
            # this main object
            # this is a dictionary with the attributes required to prefill
            # new instances of the through model
            return self.get_match_result()  # this fetches data from the session
    
        def get_extra_form_kwargs(self):
            # you could add the instance of the m2m main model here and
            # handle it in your custom form.save method
            return {'user': self.request.user}
    
        def get_queryset(self):
            # return none() if you have implemented get_initial()
            return Entry.objects.none()
            # return existing m2m relations if they exist
            # main_id = self.get_object().pk  # SingleObjectMixin or alike
            # return Entry.objects.filter(main=main_id)
    
        def formset_valid(self, formset):
            # just some example code of what you could do
            main = self.get_main_object()
            response = super().formset_valid(formset)
            main_attr_list = filter(None, [form.cleaned_data.get('entry_attr') for form in formset.forms])
            main.main_attr = sum(main_attr_list)
            main.save()
            return response
    

    表格

    through 模型的常规 Django ModelForm。就像这里的用户一样,提供对定义 m2m 字段的模型实例的引用,以便您可以在保存之前对其进行分配。

    def __init__(self, *args, user=None, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)
    
    def save(self, commit=True):
        self.instance.owner = self.user
        return super().save(commit)
    

    模板

    <form id="the-matching" method="POST"
          action="{{ save_url }}" data-session-url="{{ session_url }}">
        {% csrf_token %}
        {{ formset.management_form }}
        <ul class="match__matches">
        {% for form in formset %}
            {% include 'import_match__match.html' %}
        {% endfor %}
        </ul>
    </form>
    

    在每个表单中(在import_match__match.html 内),您都以通常的 django 方式遍历字段。这是隐藏字段的示例:

    {% for field in form %}
    {% if field.value %}
    <input type="hidden" name="{{ form.prefix }}-{{ field.name }}" value="{{ field.value }}"/>
    {% endif %}
    {% endfor %}
    

    处理主要对象的表单:

    • 您可以创建两个视图并在单击一个“保存”按钮后通过 JS 提交给它们。
    • 或者您可以提交到一个视图(如上)并在 get() 和 post() 中显式创建主对象的表单,然后在调用 formset_valid 时保存它。
    • 您也可以尝试同时实现 ModelFormsetView 和 FormView 并覆盖所有相关方法来处理这两个表单实例(formset 实例和主表单)。

    【讨论】:

    • 感谢您的建议。我被取消了这个项目,现在才回来。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-23
    • 1970-01-01
    • 1970-01-01
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多