QuerySelectField Functionality from wtforms-sqlalchemy

165 Views Asked by At

I am trying to autopopulate data from my Flask-SQLAlchemy database to a FlaskForm. Here are my code snippets:

class Products(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    name = db.Column(db.String(100), nullable=False, unique=True)
    cost = db.Column(db.Float(), nullable=False)
    price = db.Column(db.Float(), nullable=False)
    category = db.Column(db.String(20), nullable=False)
    details = db.Column(db.String(255), nullable=True)
    quantity = db.Column(db.Integer(), nullable=False, default=0)
    threshold = db.Column(db.Integer(), nullable=False, default=0)
    prod_img = db.Column(db.String(20), nullable=False, default='prod_img.jpg')
    disc_amt = db.Column(db.Float(), nullable=True)
    disc_percent = db.Column(db.Float(), nullable=True)
    disc_conditions = db.Column(db.String(255), nullable=True)
    promo_start = db.Column(db.DateTime(), nullable=True)
    promo_end = db.Column(db.DateTime(), nullable=True)

    def __repr__(self):
        return f"Products('{self.name}', '{self.price}', '{self.quantity}')"

I want to autopopulate the products from this model to a form that takes in purchases. Here are my forms:

from flask_wtf import FlaskForm
from wtforms import SubmitField, FloatField, IntegerField, FieldList, FormField, DateField
from wtforms.validators import DataRequired
from wtforms_sqlalchemy.fields import QuerySelectField
from root.models import Products, Suppliers

class ProductForm(FlaskForm):
    name = QuerySelectField('Product Name', validators=[DataRequired()], query_factory=lambda: Products.query.all(), get_label='name')
    quantity = IntegerField('Quantity', validators=[DataRequired()])
    total = FloatField('Total Cost', validators=[DataRequired()])
    submit = SubmitField('Add Product')


class ReceiptForm(FlaskForm):
    date = DateField('Date', validators=[DataRequired()])
    supplier = QuerySelectField('Supplier', validators=[DataRequired()], query_factory=lambda: Suppliers.query.all(), get_label='company')
    products = FieldList(FormField(ProductForm), min_entries=1)
    submit = SubmitField('Add Purchase')

And here is my route:

from flask import Blueprint, render_template
from root.models import Products
from root.purchases.forms import ReceiptForm


purchases = Blueprint('purchases', __name__)


@purchases.route('/add_receipt/', methods=['GET', 'POST'])
def add_receipt():
    form = ReceiptForm()
    print(type(form.products[0].name))  # Print the type of the 'name' field
    print(form.products[0].name)  # Print the 'name' field itself
    if form.validate_on_submit():
        pass
    return render_template('add_purchases.html', form=form)

And finally a snippet of my template:

<div class="card">
        <div class="card-header"><h2 class="lead">Add Purchases</h2></div>
        <div class="card-body">
            <form action="" id="purchase-form">
                {{ form.hidden_tag() }}
                <div class="mb-3">
                    {{ form.date.label }} {{ form.date(class="form-control") }}
                </div>
                <div class="mb-3">
                    {{ form.supplier.label }} {{ form.supplier(class="form-control") }}
                </div>
                {% for product_form in form.products %}
                    <div class="mb-3">
                        {{ product_form.name.label }} {{ product_form.name(class="form-control") }}
                        {{ product_form.quantity.label }} {{ product_form.quantity(class="form-control") }}
                        {{ product_form.total.label }} {{ product_form.total(class="form-control") }}
                    </div>
                {% endfor %}
                <button type="submit" class="btn btn-primary">Submit</button>
            </form>
        </div>

When I run this code, I keep getting the error below:

127.0.0.1 - - [09/Sep/2023 16:24:36] "GET /add_receipt/ HTTP/1.1" 500 -
Traceback (most recent call last):
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 2213, in __call__
    return self.wsgi_app(environ, start_response)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 2193, in wsgi_app
    response = self.handle_exception(e)
               ^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 2190, in wsgi_app
    response = self.full_dispatch_request()
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 1486, in full_dispatch_request
    rv = self.handle_user_exception(e)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 1484, in full_dispatch_request
    rv = self.dispatch_request()
         ^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\app.py", line 1469, in dispatch_request
    return self.ensure_sync(self.view_functions[rule.endpoint])(**view_args)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "x:\Code\a-FLASK\Projects\retail_pos\root\purchases\routes.py", line 16, in add_receipt
    return render_template('add_purchases.html', form=form)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\templating.py", line 151, in render_template
    return _render(app, template, context)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\flask\templating.py", line 132, in _render
    rv = template.render(context)
         ^^^^^^^^^^^^^^^^^^^^^^^^
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\jinja2\environment.py", line 1301, in render
    self.environment.handle_exception()
  File "X:\Code\a-FLASK\Projects\retail_pos\virt\Lib\site-packages\jinja2\environment.py", line 936, in handle_exception
    raise rewrite_traceback_stack(source=source)
  File "x:\Code\a-FLASK\Projects\retail_pos\root\templates\add_purchases.html", line 1, in top-level template code
    {% extends 'base.html' %}
  File "x:\Code\a-FLASK\Projects\retail_pos\root\templates\base.html", line 162, in top-level template code
    {% block content %}{% endblock content %}
  File "x:\Code\a-FLASK\Projects\retail_pos\root\templates\add_purchases.html", line 30, in block 'content'
    {{ product_form.name.label }} {{ product_form.name(class="form-control") }}
TypeError: 'str' object is not callable
127.0.0.1 - - [09/Sep/2023 16:24:36] "GET /add_receipt/?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 304 -
127.0.0.1 - - [09/Sep/2023 16:24:36] "GET /add_receipt/?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 304 -
127.0.0.1 - - [09/Sep/2023 16:24:36] "GET /add_receipt/?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 304 -
127.0.0.1 - - [09/Sep/2023 16:24:37] "GET /add_receipt/?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 304 -

Can someone please tell me where I went wrong?

I tried working with the coerce function as well but that bore me another whole mountain of errors.

1

There are 1 best solutions below

7
pwoltschk On

The error is occurring because product_form.name is a string, not a QuerySelectField as you intended.

For debugging purposes, you can print form.products[0].name before rendering the template to inspect what it contains. It should be a field object, not a string.