Flask WTForms 动态添加字段

问题描述 投票:0回答:1

第一次做开发。一直对Python充满好奇。我有 3 个嵌套的表单,我构建了一个模板来呈现表单。我已经成功地应用 id 和 name,同时使用 jquery 动态添加字段。我已经建立了一条路线。我知道也许我的代码不是最好看的代码,抱歉。我无法通过提交按钮在表中添加合同。我不知道为什么。

型号:

class Contract(BaseModel):
    supplier_id = db.Column(db.Integer, db.ForeignKey('supplier.id'))
    customer_id = db.Column(db.Integer, db.ForeignKey('customer.id'))
    incoterm_id = db.Column(db.Integer, db.ForeignKey('incoterm.id'))
    pod_id = db.Column(db.Integer, db.ForeignKey('port_of_discharge.id'))
    total_price = db.Column(db.DECIMAL(10, 2), nullable=False)
    contract_transports = db.relationship('ContractTransport', back_populates='contracts')

表格:

class ProductForm(FlaskForm):
    product = SelectField('Product', render_kw={'class': 'product'}, validators=[DataRequired()])
    quantity = IntegerField('Quantity', render_kw={'class': 'quantity'}, validators=[DataRequired()])
    price_per_unit = DecimalField('Price Per Unit', render_kw={'class': 'price_per_unit'}, validators=[DataRequired()])

class TransportForm(FlaskForm):
    transport = SelectField('Transport', render_kw={'class': 'transport'}, validators=[DataRequired()])
    products = FieldList(FormField(ProductForm), render_kw={'class': 'products_form'}, min_entries=1)

class UnifiedForm(FlaskForm):
    contract_supplier = SelectField('Supplier', render_kw={'class': 'supplier'}, validators=[DataRequired()])
    contract_customer = SelectField('Customer', render_kw={'class': 'customer'}, validators=[DataRequired()])
    contract_incoterm = SelectField('Incoterm', render_kw={'class': 'incoterm'}, validators=[DataRequired()])
    contract_pod = SelectField('POD', render_kw={'class': 'pod'}, validators=[DataRequired()])
    transports = FieldList(FormField(TransportForm), render_kw={'class': 'trasports_form'}, min_entries=1)
    submit = SubmitField('Add Contract')

路线:

@contract_flow_bp.route('/add_contract_unified', methods=['GET', 'POST'])
@login_required
def add_contract_unified():
    form = UnifiedForm()
    form.contract_supplier.choices = [(s.id, s.name) for s in Supplier.query.all()]
    form.contract_customer.choices = [(c.id, c.name) for c in Customer.query.all()]
    form.contract_incoterm.choices = [(i.id, i.incoterm_name) for i in Incoterm.query.all()]
    form.contract_pod.choices = [(p.id, p.pod_name) for p in PortOfDischarge.query.all()]
    transport_choices = [(t.id, t.type_name) for t in TransportType.query.all()]
    for transport_form in form.transports:
        transport_form.transport.choices = transport_choices
    product_choices = [(p.id, p.product_name) for p in Product.query.all()]
    for transport_form in form.transports:
        for product_form in transport_form.products:
            product_form.product.choices = product_choices
    if form.validate_on_submit() and 'submit' in request.form:
        total_price = 0  # Initialize total_price to 0
    
        # Step 1: Create and commit Contract
        new_contract = Contract(
            supplier_id=form.contract_supplier.data,
            customer_id=form.contract_customer.data,
            incoterm_id=form.contract_incoterm.data,
            pod_id=form.contract_pod.data,
            total_price=0  # Initialize to 0, we will update it later
        )
        db.session.add(new_contract)
        db.session.flush()
        flash('Step 1: Contract created.', 'info')

        # Step 2: Iterate through each TransportForm
        for transport_form in form.transports:
            new_transport = Transport(transport_type_id=transport_form.transport.data)
            db.session.add(new_transport)
            db.session.flush()
            flash('Step 2: Transport created.', 'info')

            # Step 3: Create and commit ContractTransport
            new_contract_transport = ContractTransport(
                contract_id=new_contract.id,
                transport_id=new_transport.id
            )
            db.session.add(new_contract_transport)
            db.session.flush()
            flash('Step 3: ContractTransport created.', 'info')

            # Step 4: Iterate through each ProductForm
            for product_form in transport_form.products:
                subtotal_price = product_form.quantity.data * product_form.price_per_unit.data
                total_price += subtotal_price  # Update the total_price
            
                new_contract_transport_product = ContractTransportProduct(
                    contract_transport_id=new_contract_transport.id,
                    product_id=product_form.product.data,
                    quantity=product_form.quantity.data,
                    price_per_unit=product_form.price_per_unit.data,
                    subtotal_price=subtotal_price
                )
                db.session.add(new_contract_transport_product)
                flash('Step 4: ContractTransportProduct created.', 'info')

        # Update the total_price in the Contract
        new_contract.total_price = total_price
        db.session.commit()        
        flash('Contract added successfully!', 'success')
        return redirect(url_for('contract_flow.add_contract_unified'))
    return render_template('add_contract_unified.html', form=form)

模板:

{% extends "base.html" %}
{% block content %}

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<script>

$(document).ready(function() {

    let indexproduct = 1;  // Start index at 1
    let indextransport = 1;  // Start index at 1
    
// Add more ProductForms to each TransportForm
$(document).on('click', '.addProduct', function() {
    var transportDiv = $(this).closest('.transport-div');
    var lastProductDiv = transportDiv.find(".product-div:last");
    var newProduct = lastProductDiv.clone();

    lastProductDiv.after(newProduct);
    newProduct.find("input").val("");  // Clear input fields

    // Extract the transport index and last product index from an existing id
    var sampleId = lastProductDiv.find(".product").attr("id");
    var [transportIndex, productIndex] = sampleId.match(/transports-(\d+)-products-(\d+)-/).slice(1, 3);

    // Increment the product index for new entries
    productIndex++;

    // An array of other field classes to loop through
    const fields = ['product', 'quantity', 'price_per_unit'];

    // Loop through each field to update 'id' and 'name'
    fields.forEach(function(field) {
        const newIdAndName = `transports-${transportIndex}-products-${productIndex}-${field}`;
        newProduct.find(`.${field}`).attr("id", newIdAndName);
        newProduct.find(`.${field}`).attr("name", newIdAndName);
    });
});

    
    // Add more TransportForms to UnifiedForm
    $('#addTransport').click(function() {
        var newTransport = $(".transport-div:last").clone();
        $(".transport-div:last").after(newTransport);
        $(".transport-div:last").find("input").val("");  // Clear input fields
        $(".transport-div:last").find("select").prop('selectedIndex', 0);  // Reset select elements

        // Update 'id' and 'name' for 'transport' separately
        $(".transport-div:last").find(".transport").attr("id", "transports-" + indextransport + "-transport");
        $(".transport-div:last").find(".transport").attr("name", "transports-" + indextransport + "-transport");

        // An array of other field classes to loop through
        const otherFields = ['product', 'quantity', 'price_per_unit'];

            // Loop through each field to update 'id' and 'name'
            otherFields.forEach(function(field) {
                const newIdAndName = `transports-${indextransport}-products-0-${field}`;
            $(".transport-div:last").find(`.${field}`).attr("id", newIdAndName);
            $(".transport-div:last").find(`.${field}`).attr("name", newIdAndName);
            });

            indextransport++;
    });

});

</script>

<h2>Add Contract</h2>
<form action="{{ url_for('contract_flow.add_contract_unified') }}" method="post" role= "form">
    <fieldset>
        <legend>Contract Header</legend>
        {{ form.hidden_tag() }}
        {{ form.contract_supplier.label }} {{ form.contract_supplier }}
        {{ form.contract_customer.label }} {{ form.contract_customer }}
        {{ form.contract_incoterm.label }} {{ form.contract_incoterm }}
        {{ form.contract_pod.label }} {{ form.contract_pod }}
    </fieldset><br>
    {% for transport in form.transports %}
        <fieldset>
            <legend>Transport & Product Details</legend>
                <div class="transport-div">
                    {% for product in transport.products %}
                    {{ transport.hidden_tag() }}
                    <table class="transport-table">
                        <tbody>
                            <tr>
                                <td>
                                    {{ transport.transport }}
                                </td>
                                <td>
                                    <div class="product-div">
                                    <table>
                                        <tbody>
                                            <tr>
                                                <td>
                                                    {{ product.product }}
                                                </td>
                                                <td>
                                                    {{ product.quantity }}
                                                </td>
                                                <td>
                                                    {{ product.price_per_unit }}
                                                </td>
                                                <td>
                                                    <button type="button" class="addProduct">Add Another Product</button>
                                                </td>
                                            </tr>
                                        </tbody>
                                    </table>
                                </div>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                    {% endfor %}
                </div>
        </fieldset>
    {% endfor %}
<button type="button" id="addTransport">Add Another Transport</button>
    {{ form.submit }}
</form>

{% with messages = get_flashed_messages(with_categories=true) %}
  {% if messages %}
    <ul class=flashes>
    {% for category, message in messages %}
      <li class="{{ category }}">{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

{% endblock %}

我认为最终设法为呈现的表单字段提供正确的 ID 和名称就足以添加新合同。然而事实并非如此。我还不知道如何调试,所以不知道错误在哪里。请帮忙!

我知道我可以在客户端不使用 Flask-WTForms 而仅使用 jquery 来 100% 动态地完成此表单(我在这之前构建了一个表单),但我想使用 Flask-WTForms 来学习这种方式。

flask flask-wtforms wtforms
1个回答
0
投票

天哪!对于 ensted 表单,我只需从“FlaskForm”切换到“Form”。从这里得到的信息: 如何在FLASK中使用FieldList-WTF

© www.soinside.com 2019 - 2024. All rights reserved.