Forms with relations

WTForms-Alchemy provides special Field subtypes ModelFormField and ModelFieldList. When using these types WTForms-Alchemy understands model relations and is smart enough to populate related objects accordingly.

One-to-one relations

Consider the following example. We have Event and Location classes with each event having one location.

from sqlalchemy.ext.declarative import declarative_base
from wtforms_alchemy import ModelForm, ModelFormField

Base = declarative_base()


class Location(Base):
    __tablename__ = 'location'
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    name = sa.Column(sa.Unicode(255), nullable=True)

class Event(Base):
    __tablename__ = 'event'
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.Unicode(255), nullable=False)
    location_id = sa.Column(sa.Integer, sa.ForeignKey(Location.id))
    location = sa.orm.relationship(Location)

class LocationForm(ModelForm):
    class Meta:
        model = Location

class EventForm(ModelForm):
    class Meta:
        model = Event

    location = ModelFormField(LocationForm)

Now if we populate the EventForm, WTForms-Alchemy is smart enough to populate related location too.

event = Event()
form = EventForm(request.POST)
form.populate_obj(event)

One-to-many relations

Consider the following example. We have Event and Location classes with each event having many location. Notice we are using FormField along with ModelFieldList.

from sqlalchemy.ext.declarative import declarative_base
from wtforms_alchemy import ModelForm, ModelFieldList
from wtforms.fields import FormField

Base = declarative_base()


class Event(Base):
    __tablename__ = 'event'
    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.Unicode(255), nullable=False)


class Location(Base):
    __tablename__ = 'location'
    id = sa.Column(sa.Integer, autoincrement=True, primary_key=True)
    name = sa.Column(sa.Unicode(255), nullable=True)

    event_id = sa.Column(sa.Integer, sa.ForeignKey(Event.id))
    event = sa.orm.relationship(
        Location,
        backref='locations'  # the event needs to have this
    )


class LocationForm(ModelForm):
    class Meta:
        model = Location


class EventForm(ModelForm):
    class Meta:
        model = Event

    locations = ModelFieldList(FormField(LocationForm))

Now if we populate the EventForm, WTForms-Alchemy is smart enough to populate related locations too.

event = Event()
form = EventForm(request.POST)
form.populate_obj(event)