0
votes

I am pulling my hair out with this as I haven't had any issues with flask or WTForms and have built quite a large application so far. Here is what I am trying to do.

I have 2 data models, Subscription, and Billing interval. There is a foreign key between the two and everything works well:

class Subscriptions(db.Model):

    __tablename__ = 'subscriptions'

    id = db.Column(db.Integer, primary_key = True)
    name = db.Column(db.String(64))
    description = db.Column(db.String(255))
    lastcrawled = db.Column(db.Date)
    lastcheckedby = db.Column(db.Integer)
    isActive = db.Column(db.Boolean, default = False)
    datecreated = db.Column(db.Date, default = datetime.date.today())
    pricing = db.Column(db.Numeric(10, 2), nullable=True)
    alias = db.Column(db.String(64))
    isQuote = db.Column(db.Boolean, default = True)

    billinginterval_id = db.Column(db.Integer, db.ForeignKey('billinginterval.id'))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))

class BillingInterval(db.Model):
    __tablename__ = 'billinginterval'

    id = db.Column(db.Integer, primary_key = True)
    interval = db.Column(db.String(64))
    subscriptions = db.relationship('Subscriptions', backref='billinginterval')

    def __str__(self):
        return self.interval

I have a form where a user can add another subscription:

class AddSubscription(FlaskForm):

    name = StringField("Name", validators=[DataRequired()])
    description = TextAreaField('Description')
    pricing = IntegerField('Pricing')
    billinginterval = SelectField('Billing Interval',choices=BillingInterval.query.all())
    submit = SubmitField("Add Subscription")

And finally here is my Template:

{% extends "layout.html" %}
{% block content %}
    <form method="POST">
        {# This hidden_tag is a CSRF security feature. #}
        {{ form.hidden_tag() }}
        {{ form.name.label }} {{ form.name() }}<br>
        {{ form.description.label }} {{ form.description() }}<br>
        {{ form.pricing.label }} {{ form.pricing() }}<br>
        {{ form.billinginterval.label }} {{ form.billinginterval() }}<br>
        {{ form.submit() }}
    </form>
{% endblock %}

I need to have a "SelectField" that populates from the "billinginterval" table (displays interval as the choices) and then writes the billinginterval.id field to the subscription model.

Everything works flawlessly until I try adding this billing interval dropdown. Just can't get it.

I've tried everything, I cannot get it to work. Any help would be grateful. Thanks!

2

2 Answers

0
votes

Ok.... I figured this out. Not sure it is the best way.

In my Forms.py file I modified the select field:

class AddSubscription(FlaskForm):

    name = StringField("Name", validators=[DataRequired()])
    description = TextAreaField('Description')
    pricing = IntegerField('Pricing')
    #Right HERE!!!!
    billinginterval = SelectField('Billing Interval',choices=[], coerce=int, validate_choice=True)
    #billinginterval = SelectField('Billing Interval',choices=[(1,'Monthly'),(2,'Daily')])
    #billinginterval = QuerySelectField(query_factory=lambda: BillingInterval.query.all())
    #billinginterval = QuerySelectField(query_factory=billinginterval_query, allow_blank=True)
    submit = SubmitField("Add Subscription")

I added ",choices=[], coerce=int, validate_choice=True)" to the billinginterval form field.

Then in my views.py (routing file) I set the choices variable:

def addsubscription(userid):
    form = AddSubscription()

    form.billinginterval.choices = [(interval.id, interval.interval) for interval in BillingInterval.query.all()]
    #print(form.billinginterval.data)
    if form.validate_on_submit():

        subscription = Subscriptions(name=form.name.data,
                    description=form.description.data,
                    lastcheckedby=current_user.id,
                    isActive=True,
                    user_id=userid,
                    billinginterval_id=form.billinginterval.data,
                    alias = random.getrandbits(32))    

        db.session.add(subscription)
        db.session.commit()

        subscriptions = Subscriptions.query.all()

        #flash('Thanks for registering! Now you can login!')
        return redirect(url_for('admin.managesubscriptions', userid=userid))
    return render_template('addsubscription.html', form=form)
    ```
And loaded it manually from my data model.

All works now. Hope this solution helps someone!
0
votes

The best practice is to load the choices in the __init__ method of your AddSubscription class:

class AddSubscription(FlaskForm):
    name = StringField("Name", validators=[DataRequired()])
    description = TextAreaField('Description')
    pricing = IntegerField('Pricing')
    billinginterval = SelectField('Billing Interval', coerce=int)
    submit = SubmitField("Add Subscription")

    def __init__(self, *args, **kwargs):
        super(AddSubscription, self).__init__(*args, **kwargs)
        self.billinginterval.choices = [(interval.id, interval.interval) 
                                        for interval in BillingInterval.query.all()]

Then you will not load the choices in every view functions where you will use AddSubscription form.