【问题标题】:One form for two models in djangodjango 中两种模型的一种形式
【发布时间】:2021-04-13 03:57:54
【问题描述】:
Django==3.1.7
django-crispy-forms==1.11.2

我有 2 个模型:Order 和 OrderList
Order 是标题,OrderList 是相关 Order 的表格部分

class Order(models.Model):
    print_number = models.PositiveIntegerField(
            verbose_name=_("Number"),
            default=get_todays_free_print_number,
        )
    # ... some other fields 

class OrderList(models.Model):
    order = models.ForeignKey(
            Order,
            blank=False,
            null=False,
            on_delete=models.CASCADE
        )
    item = models.ForeignKey(
            Item,
            verbose_name=_("item"),
            blank=True,
            null=True,
            on_delete=models.CASCADE
        )
    # ... some other OrderList fields

问题是如何创建一个包含两个模型的表单并提供将 Order 中的 OrderList 位置添加到表单中的能力 并保存它们。

我做了什么:
forms.py - 我为 OrderList 使用了内联表单集工厂

from django.forms import ModelForm
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from .models import Order, OrderList

class OrderForm(ModelForm):
    class Meta:
        model = Order
        fields = [
            '__all__',
        ]

class OrderListForm(ModelForm):
    class Meta:
        model = OrderList
        fields = [
            '__all__',
        ]

class OrderListFormSetHelper(FormHelper):
    """Use class to display the formset as a table"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.template = 'bootstrap4/table_inline_formset.html'
        
        # I am not sure we should add a button here
        ####################################################
        self.add_input(Submit('submit', 'Submit',
                               css_class='btn btn-primary offset4'))

views.py

@login_required
def orders(request):

    template = f'{APP_NAME}/index.html'

    list_helper = OrderListFormSetHelper()

    list_formset = inlineformset_factory(Order,
                                         OrderList,
                                         OrderListForm,)

    if request.method == 'POST':

        form = OrderForm(request.POST, prefix="header")

        if form.is_valid() and list_formset.is_valid():
            order = form.save()

            order_list = list_formset.save(commit=False)
            order_list.order = order
            order_list.save()

            return HttpResponseRedirect(reverse('order_created'))

    else:  # all other methods means we should create a blank form
        form = OrderForm()

    
    return render(request, template, {'form': form,
                                      'list_form': list_formset,
                                      'list_helper': list_helper})

index.html

<form method="post">
    {% csrf_token %}
    {% crispy form  %}
    {% crispy list_form list_helper %}

    <!-- the button below doesn't make sense because it does nothing.
          the self.add_input in forms.py already adds a submit button.
               -->
    <button type="submit" class="btn btn-primary">
        {% translate "Send an order" %}
    </button>

</form>

生成的 html 会像这样呈现页面:

但是当我按下提交按钮时 它清理订单相关字段并将它们标记为空白

【问题讨论】:

  • 你想在forms.py的order下添加orderlist吗??
  • @raven404 是的,我愿意
  • 你能分享你的模型文件吗?

标签: python django django-crispy-forms


【解决方案1】:

您使用crispy 模板标签来呈现您的表单。它使用FormHelper 类来帮助呈现您的表单,默认情况下将属性form_tag 设置为True,这使它为您呈现form 标记。这意味着您正在 嵌套 表单标签,这在 HTML5 标准中不起作用并且是不可能的。您需要将此属性设置为False 以防止出现这种情况:

class OrderForm(ModelForm):
    class Meta:
        model = Order
        fields = [
            '__all__',
        ]
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.helper = FormHelper(self) # Explicitly set helper to prevent automatic creation
        self.helper.form_tag = False # Don't render form tag
        self.helper.disable_csrf = True # Don't render CSRF token

接下来,在您在视图中创建的助手中,您还必须设置这些属性。此外,您所说的list_formset 不是表单集的instance,而是,因此您实际上需要实例化表单集类并使用它:

@login_required
def orders(request):

    template = f'{APP_NAME}/index.html'

    list_helper = OrderListFormSetHelper()
    list_helper.form_tag = False # Don't render form tag
    list_helper.disable_csrf = True # Don't render CSRF token

    OrderListFormSet = inlineformset_factory(Order,
                                         OrderList,
                                         OrderListForm,)

    if request.method == 'POST':

        form = OrderForm(request.POST, prefix="header")
        list_formset = OrderListFormSet(request.POST, instance=form.instance) # Instantiate formset

        if form.is_valid() and list_formset.is_valid():
            order = form.save()

            order_list = list_formset.save()
            # Remove below two line, have already instantiated formset with `form.instance` and called save without `commit=False`
            # order_list.order = order
            # order_list.save()

            return HttpResponseRedirect(reverse('order_created'))

    else:  # all other methods means we should create a blank form
        form = OrderForm()
        list_formset = OrderListFormSet(instance=form.instance) # Instantiate formset

    
    return render(request, template, {'form': form,
                                      'list_form': list_formset,
                                      'list_helper': list_helper})

【讨论】:

    猜你喜欢
    • 2011-09-28
    • 1970-01-01
    • 2013-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-12
    • 1970-01-01
    • 2011-02-03
    相关资源
    最近更新 更多