python - example - ¿Actualizar las relaciones de muchos a muchos usando Flask, SQLAlchemy y WTForms?
python web form (1)
Quiero crear un formulario para relaciones de muchos a muchos usando Flask, SQLAlchemy y WTForms que representa estos modelos:
personaddress = db.Table(''personaddress'',
db.Column(''person'', db.Integer, db.ForeignKey(''person.id'')),
db.Column(''address'', db.Integer, db.ForeignKey(''address.id''))
)
class Person(db.Model):
__tablename__ = "person"
id = db.Column(Integer, primary_key=True)
name = db.Column(String, nullable=False)
addresses = db.relationship(''Address'', secondary=personaddress, backref=db.backref(''person'', lazy=''dynamic''))
class Address(db.Model):
__tablename__ = "address"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
Requerimientos
Ahora quiero crear una sola página que contenga formularios para lograr lo siguiente:
- agregar / editar / borrar una persona
- agregar / editar / borrar una dirección
- agregar / editar / eliminar una relación entre una persona y una dirección
Requisito importante: al usar QuerySelectField, puedo elegir direcciones existentes para una persona. Pero quiero agregar nuevas direcciones en la misma forma.
He jugado un poco con model_form
para los principales modelos y subformularios utilizando FormField
para la tabla de unión, pero simplemente no puedo averiguar cómo actualizar todo, incluidas las relaciones de clave externa. La página debe tener un solo botón de envío para todos los formularios y subformularios mostrados.
Preguntas
- ¿Cómo se implementan normalmente los requisitos anteriores en Flask?
- ¿Es este escenario de muchos a muchos algo que Django puede manejar más fácilmente a través de su interfaz de administración?
También he encontrado algo similar antes. Traté de resolverlo usando model_form, pero no soluciona el problema de agregar nuevas entradas dinámicamente, y me estaba costando mucho usarlo al tratar con las relaciones.
El uso de QuerySelectField en WTForms solo lo ayudará a completar, por ejemplo, un <select> con id, pares de valores correspondientes a las direcciones existentes. Pero todavía se procesa en un formulario html regular en la plantilla.
Al utilizar algún tipo de selección múltiple con la posibilidad de agregar dinámicamente nuevas opciones en la interfaz, puede enviar direcciones adicionales de la misma forma. El punto final se encargará de crear nuevas direcciones si no existen en la base de datos.
El formulario WTForm sería:
from app import db
class PersonAddressForm(Form):
id = HiddenField(''id'')
name = StringField(''Name'')
addresses = QuerySelectField(''Addresses'',
query_factory=lambda: db.session.query(Address),
get_pk=lambda a: a.id, get_label=lambda a: a.name)
# Custom validate
def validate(self):
# ... custom validation
return True
Y la ruta algo como:
# ... this will be used to create and update a user
@route(''create/<userid>'', methods=["GET"])
def get_user_form(userid):
# ... Get the Person
user = Person()
if userid:
# ... if userid supplied, use existing Person object
user = Person.query.get(userid)
# ... Populate the form
person_form = PersonAddressForm(obj=user)
# ... return form
return render_template(''somepage.html'', form=person_form)
@route(''create/<userid>'', methods=["POST"])
def post_person_form(userid):
person_form = PersonAddressForm(request.form)
if person_form.validate():
# ... Get db object
person = db.session.query(Person).get(form.id)
# ... Add changes to the object from the form
person_form.populate_obj(obj=person_address)
# ... Get addresses
addresses = form.addresses.raw_data
# ... loop over and add to person
for address in addresses:
# Add or create an address
actual_address = db.session.query(Address).get(address.id)
# ... check if address is existing
if not actual_address:
# ... if address not existing, create new one
actual_address = Address(address.name)
db.session.add(actual_address)
# ... Append new or created address to person
person.addresses.append(actual_address)
# ... save changes to the db
db.session.commit()
# ... Update/Create complete
return redirect(url_for(''get_users''))
else:
# ... form not valid, notify user
# ...
Esto manejará editar / crear usuario y crear dirección. Además de crear la relación entre. Para que sea también compatible con eliminar dirección, cambiar
person.addresses.append(actual_address)
a
person.addresses = list_of_actual_addresses
y cambia esto en el modelo de persona (cascade = ''delete-huérfano'')
addresses = db.relationship(''Address'', secondary=personaddress, cascade=''delete-orphan'' backref=db.backref(''person'', lazy=''dynamic''))
Esto hará que el formulario actualice la relación de direcciones completa cada vez y la cascada eliminará las direcciones huérfanas. Por lo tanto, la lista completa de direcciones de una persona se actualizaría cada vez que se envíe el formulario.
Al tratar con WTForms en plantillas, recomiendo usar macros si aún no lo ha hecho. Tendrías que reescribirlo hasta cierto punto, pero mira this .
Espero que esto ayude