首页 > 解决方案 > inlineformset_factory:验证失败(此字段为必填项)

问题描述

我有带有 3 个字段的 inlineformset_factory 表单。

但是当我更新表单时,会创建一个新的空白行。

如果我决定将此行留空,则验证失败,因为需要字段

所以问题来自 inlineformset_factory MenuFormset 中的“额外”参数。我考虑过使用带有 extra=0 的特定 inlineformset_factory UpdateMenuFormset 用于更新视图,但这可能不是一个好方法;此外,当我添加一个表单集时,它会使用预填充值而不是空表单集进行表单设置...

如何管理此验证?

模型.py

class Orders(SafeDeleteModel):
    """ A class to create a new order instance in the Schitt's Creek Cafe Tropical. """

    _safedelete_policy = SOFT_DELETE_CASCADE
    order_id = models.AutoField("Order id", primary_key = True)
    table = models.ForeignKey(Tables, on_delete = models.CASCADE) # related table
    customers = models.IntegerField("Number of customer", null=True, blank=True)
    split_bill = models.IntegerField("Split bill", null=True, blank=True)
    delivered = models.BooleanField("Delivered", null=True, blank=True, default=False)
    paid = models.BooleanField("Available", null=True, blank=True, default=False)
    created_at = models.DateTimeField("Date created", auto_now_add = True)
    log = HistoricalRecords()


class Orders_Menus(SafeDeleteModel):
    """ A class to create a new Orders_Menus instance in the Schitt's Creek Cafe Tropical. """
    
    _safedelete_policy = SOFT_DELETE_CASCADE
    order = models.ForeignKey("Orders", on_delete = models.CASCADE)
    menu = models.ForeignKey("Menus", on_delete = models.CASCADE)
    cooking = models.IntegerField("Cooking", default=0, null=True, blank=True)
    tone = models.IntegerField("Tone", default=0, null=True, blank=True)
    log = HistoricalRecords()

    class Meta:

        db_table = 'Orders_Menus'

表格.py

MenuFormset = inlineformset_factory(
    Orders, Orders_Menus, 
    fields=('cooking','menu','tone'),
    widgets={
        'cooking': forms.Select(choices = COOKING),
        'tone': forms.Select(choices = TONES),
        'menu': forms.Select(choices = MENUS),
    },
    extra=1,
    can_delete=True,
)

order_update.html

{% extends 'layouts/base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block extrahead %}{% endblock %}
{% block title %}Order | Schitt's Creek Cafe Tropical{% endblock %}
{% block content %}

<style>
.addspace {
    padding-bottom:10px;
}

</style>

<div class='container'>
    <br>        
    <br>
    <h1>Complete order - Update</h1>
    <br>
    <br>
    <br>
    <form id="ordereditform" method="POST" class="post-form">
        {% csrf_token %}
        {{ form|crispy }}

        {% comment %} <a style="margin-right: 40px" data-target="" class="" href="#">New Menu</a> {% endcomment %}
        <br>
        <div class="dropdown-divider"></div>
        <h2>Menus</h2><br><br>
        <!-- {{ menu|crispy }} --> <!-- curieusement si j'enlève cette ligne le style crispy disparait -->
        {{ menu.management_form|crispy }}
        
            {% if menu.non_form_errors %}
            <div id="non_form_errors" class="alert alert-block alert-danger" >
                {% for error in menu.non_form_errors %}
                    <ul>
                        <li>{{ error|escape }}</li>
                    </ul>
                {% endfor %}
            </div>
            {% endif %} 
        
        <div class="row">
            <div class="col-4"><p>Item</p></div>
            <div class="col-3"><p>Cooking</p></div>
            <div class="col-4"><p>Tone</p></div>
            <div class="col-1"><p></p></div>
        </div>      
        {% for menu_form in menu %}
        
        <div class="row link-formset addspace">
            <div class="col-4">
                    
                    {{ menu_form.menu }}
                    {% if menu_form.menu.errors %}
                        {% for error in menu_form.menu.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}
            
            </div>
            {% if menu_form.instance.pk %}{{ menu_form.DELETE }}{% endif %}
            <div class="col-3">
                    
                    {{ menu_form.cooking }}
                    {% if menu_form.cooking.errors %}
                        {% for error in menu_form.cooking.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}

            </div>
            <div class="col-4">

                    {{ menu_form.tone }}
                    {% if menu_form.tone.errors %}
                        {% for error in menu_form.tone.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}

            </div>
            <div class="hidden">{{ menu_form.id }}</div>

        </div>

        {% endfor %}


        <br><br>
        <button id="ajouter_projet" class="btn btn-primary" type="submit" style="width: 100px;">Save</button>
        <a data-modal data-target="" class="btn btn-danger" href="{% url 'cafe:index' %}" style="width: 100px;">Cancel</a>
    </form>


    <!-- Modal -->
    <div class="modal fade" id="order_edit" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLongTitle">Schitt's Creek Cafe Tropical</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
            </div>
            <div class="modal-body">
            Do you valid order?
            </div>
            <div class="modal-footer">
            <button id="order_button_no" type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
            <button id="order_button_yes" data-redirect-url="" type="button" class="btn btn-primary">Yes</button>
            </div>
        </div>
        </div>
    </div>
</div>

{% endblock %}
{% block extrabody %}
<!--DataTable-->
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css">
<!--DataTable-->

<!--Modal-->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!--Modal-->

<script src="{% static 'cafe/js/jquery.formset.js' %}"></script>
<script type="text/javascript">
    var popup_order;
    var menunumber_subform; // nombre de sous formulaire affichés (1 par défaut puis ajout sur click .add-row)
    var select_app;
    var select_access;

    $('.link-formset').formset({
        addText: 'Add',
        deleteText: 'Delete',
        prefix: '{{ menu.prefix }}',
        //hideLastAddForm: 'true',

    });

    

    $(document).ready(function() {

        var prevent = false;
        var menunumber_subform = menunumber(); // nombre de sous formulaire affichés (1 par défaut au chargement de la page)
        var menuinitialnumber_subform = menuinitialnumber();

        // to add empty formset (by default, it clone previous frormset with its values)
        $('.add-row').on('click',function(event){
            i = menuinitialnumber_subform;
            $('#id_orders_menus_set-'+i+'-menu').val('');
            $('#id_orders_menus_set-'+i+'-cooking').val('');
            $('#id_orders_menus_set-'+i+'-tone').val('');
            menuinitialnumber_subform++;
        })

        $("#ordereditform").submit(function(event) {
            if(!prevent){
                event.preventDefault();
            }
            
            popup_order = $(this);
            $('#order_edit').modal('show');

        });

        $("body")
        .on('click','#order_button_yes',function(event){
            prevent = true;
            popup_order.submit();
        })


    });


    // déterminer le nombre de sous-formulaire menu présents sur la page
    function menunumber() {
        return $("#id_orders_menus_set-TOTAL_FORMS").val();
    };

    // déterminer le nombre de sous-formulaire menu présents sur la page
    function menuinitialnumber() {
        return $("#id_orders_menus_set-INITIAL_FORMS").val();
    };


</script>
{% endblock %}

order_edit.html

{% extends 'layouts/base.html' %}
{% load static %}
{% load crispy_forms_tags %}
{% block extrahead %}{% endblock %}
{% block title %}Order | Schitt's Creek Cafe Tropical{% endblock %}
{% block content %}
<style>
.addspace {
    padding-bottom:10px;
}

</style>
<div class='container'>
    <br>        
    <br>
    <h1>Complete order</h1>
    <br>
    <br>
    <br>
    <form id="ordereditform" method="POST" class="post-form" data-tables="{{ tables }}">
        {% csrf_token %}
        {{ form|crispy }}

        {% comment %} <a style="margin-right: 40px" data-target="" class="" href="#">New Menu</a> {% endcomment %}
        <br>
        <div class="dropdown-divider"></div>
        <h2>Menus</h2><br><br>
        <!-- {{ menu|crispy }} --> <!-- curieusement si j'enlève cette ligne le style crispy disparait -->
        {{ menu.management_form|crispy }}
        
            {% if menu.non_form_errors %}
            <div id="non_form_errors" class="alert alert-block alert-danger" >
                {% for error in menu.non_form_errors %}
                    <ul>
                        <li>{{ error|escape }}</li>
                    </ul>
                {% endfor %}
            </div>
            {% endif %} 
        
        <div class="row">
            <div class="col-4"><p>Item</p></div>
            <div class="col-3"><p>Cooking</p></div>
            <div class="col-4"><p>Tone</p></div>
            <div class="col-1"><p></p></div>
        </div>      
        {% for menu_form in menu %}
        
        <div class="row link-formset addspace">
            <div class="col-4">
                    
                    {{ menu_form.menu }}
                    {% if menu_form.menu.errors %}
                        {% for error in menu_form.menu.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}
            
            </div>
            {% if menu_form.instance.pk %}{{ menu_form.DELETE }}{% endif %}
            <div class="col-3">
                    
                    {{ menu_form.cooking }}
                    {% if menu_form.cooking.errors %}
                        {% for error in menu_form.cooking.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}

            </div>
            <div class="col-4">

                    {{ menu_form.tone }}
                    {% if menu_form.tone.errors %}
                        {% for error in menu_form.tone.errors %}
                        <span style="color:red;">{{ error|escape }}</span>
                        {% endfor %}
                    {% endif %}

            </div>
            <div class="hidden">{{ menu_form.id }}</div>
            
        </div>

        {% endfor %}


        <br><br>
        <button id="ajouter_projet" class="btn btn-primary" type="submit" style="width: 100px;">Save</button>
        <a data-modal data-target="" class="btn btn-danger" href="{% url 'cafe:index' %}" style="width: 100px;">Cancel</a>
    </form>


    <!-- Modal -->
    <div class="modal fade" id="order_edit" tabindex="-1" role="dialog" aria-labelledby="exampleModalCenterTitle" aria-hidden="true">
        <div class="modal-dialog modal-dialog-centered" role="document">
        <div class="modal-content">
            <div class="modal-header">
            <h5 class="modal-title" id="exampleModalLongTitle">Schitt's Creek Cafe Tropical</h5>
            <button type="button" class="close" data-dismiss="modal" aria-label="Close">
                <span aria-hidden="true">&times;</span>
            </button>
            </div>
            <div class="modal-body">
            Do you valid order?
            </div>
            <div class="modal-footer">
            <button id="order_button_no" type="button" class="btn btn-secondary" data-dismiss="modal">No</button>
            <button id="order_button_yes" data-redirect-url="" type="button" class="btn btn-primary">Yes</button>
            </div>
        </div>
        </div>
    </div>
</div>

{% endblock %}
{% block extrabody %}
<!--DataTable-->
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/jquery.dataTables.min.js"></script>
<script type="text/javascript" charset="utf8" src="https://cdn.datatables.net/1.10.20/js/dataTables.bootstrap4.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.1.3/css/bootstrap.css">
<link rel="stylesheet" type="text/css" href="https://cdn.datatables.net/1.10.20/css/dataTables.bootstrap4.min.css">
<!--DataTable-->

<!--Modal-->
<link rel="stylesheet" href="//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.js"></script>
<!--Modal-->

<script src="{% static 'cafe/js/jquery.formset.js' %}"></script>
<script type="text/javascript">
    var popup_order;
    var menunumber_subform; // nombre de sous formulaire affichés (1 par défaut puis ajout sur click .add-row)
    var select_app;
    var select_access;


    $('.link-formset').formset({
        addText: 'Add',
        deleteText: 'Delete',
        prefix: '{{ menu.prefix }}',
        //hideLastAddForm: 'true'
    });



    $(document).ready(function() {

        var prevent = false;
        var menunumber_subform = menunumber(); // nombre de sous formulaire affichés (1 par défaut au chargement de la page)


        var tables = {{ tables }}
        //https://codepen.io/Lighty_46/pen/gpLZWx?editors=1010
        // When an option is changed, search the above for matching choices
        $('#id_table').on('change', function() {
            
            // Set selected option as variable
            var selectValue = $(this).val();

            // Empty the target field
            $('#id_customers').empty();
            
            // For each choice in the selected option
            for (i = 0; i < tables[selectValue].length; i++) {
                // Output choice in the target field
                $('#id_customers').append("<option value='" + tables[selectValue][i] + "'>" + tables[selectValue][i] + "</option>");
            }
            
        });

        $("#ordereditform").submit(function(event) {
            if(!prevent){
                event.preventDefault();
            }
            
            popup_order = $(this);
            $('#order_edit').modal('show');

        });

        $("body")
        .on('click','#order_button_yes',function(event){
            prevent = true;
            popup_order.submit();
        })


    });


    // déterminer le nombre de sous-formulaire menu présents sur la page
    function menunumber() {
        return $("#id_orders_menus_set-TOTAL_FORMS").val();
    };

</script>
{% endblock %}

标签: djangoinline-formset

解决方案


推荐阅读