簡體   English   中英

django中兩種型號的一種形式

[英]One form for two models in django

Django==3.1.7
django-crispy-forms==1.11.2

我有 2 個模型:Order 和 OrderList
Order 是 header,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'))

視圖.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})

索引.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 呈現頁面如下: 提交前的頁面

但是當我按下提交按鈕時,它會清理與訂單相關的字段並將它們標記為空白在此處輸入圖像描述

您使用crispy模板標簽來呈現您的 forms。 它使用FormHelper class 來幫助呈現您的 forms,默認情況下,它的屬性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 ,而是class ,因此您實際上需要實例化表單集 class 並使用它:

@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})

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM