I have created relationship between two models - Addproduct and Brand. I want to add items in AddProducts form however I have no clue how to add brand_id in this AddProducts form. Kindly help me to get this sorted.
class Addproduct(db.Model):
#__tablename__ = 'addproduct'
id:Mapped[int] = mapped_column(Integer,primary_key=True)
name:Mapped[str] = mapped_column(String(80),nullable=False, unique=True)
price:Mapped[int] = mapped_column(Integer,nullable=False)
brand_id:Mapped[int]= mapped_column(Integer, db.ForeignKey('brand.id'),nullable=False)
brand = relationship('Brand', back_populates="product")
class Brand(db.Model):
__tablename__ = 'brand'
id:Mapped[int] = mapped_column(Integer,primary_key=True)
name:Mapped[str] = mapped_column(String(30),nullable=False, unique=True)
product = relationship('Addproduct', back_populates="brand")
class AddProducts(FlaskForm):
name = StringField('Name',validators=[DataRequired()])
price = IntegerField('Price',validators=[DataRequired()])
brands = QuerySelectField('Add a brand', query_factory=brandchoices)
Submit = SubmitField('Add Product')
I want to know how to add this brand_id in this below form:
@app.route('/addproduct', methods=['POST','GET'])
def addproduct():
form = AddProducts()
if form.validate_on_submit():
name = form.name.data
price = form.price.data
brand =
addprod = Addproduct(
name=name, price=price)
db.session.add(addprod)
db.session.commit()
flash(f'The product {name} has been added.')
return render_template('products/addproduct.html', form=form)
The QuerySelectField returns an object of type Addproduct upon request, which also gives you access to the attribute id.
But you can also simplify the code a little.
If the attributes in the database model and the form are named the same, you can use form.populate_obj(obj)
to assign the properties to the new entry.
This means that the object selected in the input field can be assigned directly to the relationship.
The following example shows you how.
from flask import (
Flask,
flash,
redirect,
render_template,
request,
url_for
)
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy.orm import (
DeclarativeBase,
Mapped
)
from typing import List
from flask_wtf import FlaskForm
from wtforms import (
StringField,
SubmitField,
IntegerField
)
from wtforms.validators import DataRequired
from wtforms_sqlalchemy.fields import QuerySelectField
class Base(DeclarativeBase):
pass
app = Flask(__name__)
app.config.from_mapping(
SECRET_KEY='your secret here',
SQLALCHEMY_DATABASE_URI='sqlite:///example.db'
)
db = SQLAlchemy(app, model_class=Base)
class Product(db.Model):
id:Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
name:Mapped[str] = db.mapped_column(db.String(80), nullable=False, unique=True)
price:Mapped[int] = db.mapped_column(db.Integer, nullable=False)
brand_id:Mapped[int]= db.mapped_column(db.Integer, db.ForeignKey('brand.id'), nullable=False)
brand:Mapped['Brand'] = db.relationship(back_populates='product')
class Brand(db.Model):
id:Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
name:Mapped[str] = db.mapped_column(db.String(30), nullable=False, unique=True)
product:Mapped[List['Product']] = db.relationship(back_populates='brand')
class AddProductForm(FlaskForm):
name = StringField('Name',validators=[DataRequired()])
price = IntegerField('Price',validators=[DataRequired()])
brand = QuerySelectField('Add a brand',
query_factory=lambda: db.session.execute(db.select(Brand)).scalars(),
get_label='name'
)
submit = SubmitField('Add Product')
with app.app_context():
db.drop_all()
db.create_all()
brands = [Brand(name=f'Brand-{i}') for i in range(1, 10)]
db.session.add_all(brands)
db.session.commit()
@app.route('/addproduct', methods=['POST','GET'])
def addproduct():
form = AddProductForm()
if form.validate_on_submit():
product = Product()
form.populate_obj(product)
db.session.add(product)
db.session.commit()
flash(f'The product {product.name} has been added.')
return redirect(url_for('addproduct'))
return render_template('products/addproduct.html', **locals())
There are two options for uploading files.
Both require the enctype 'multipart/form-data' for the form
element. The form is the same for both variants.
class AddProductForm(FlaskForm):
name = StringField('Name',validators=[DataRequired()])
price = IntegerField('Price',validators=[DataRequired()])
brand = QuerySelectField('Add a brand',
query_factory=lambda: db.session.execute(db.select(Brand)).scalars(),
get_label='name'
)
image_1 = FileField('First Image',
validators=[
FileRequired(),
FileAllowed(['jpg','jpeg','png','gif'])
]
)
submit = SubmitField('Add Product')
In this case, the files, provided they are saved in the static folder, can be accessed via the file name.
<img src="{{ url_for('static', filename=product.image_ref1) }}"/>
class Product(db.Model):
id:Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
name:Mapped[str] = db.mapped_column(db.String(80), nullable=False, unique=True)
price:Mapped[int] = db.mapped_column(db.Integer, nullable=False)
brand_id:Mapped[int]= db.mapped_column(db.Integer, db.ForeignKey('brand.id'), nullable=False)
brand:Mapped['Brand'] = db.relationship(back_populates='product')
image_ref1:Mapped[str] = db.mapped_column(db.String)
@app.route('/addproduct', methods=['POST','GET'])
def addproduct():
form = AddProductForm()
if form.validate_on_submit():
file = form.image_1.data
filename = secure_filename(file.filename)
file.save(os.path.join(
app.static_folder, filename
))
product = Product(image_ref1 = filename)
form.populate_obj(product)
db.session.add(product)
db.session.commit()
flash(f'The product {product.name} has been added.')
return redirect(url_for('addproduct'))
return render_template('products/addproduct.html', **locals())
Two columns are required, one containing the data and one containing the mime type. In order to serve the image data again, an additional endpoint is required.
class Product(db.Model):
id:Mapped[int] = db.mapped_column(db.Integer, primary_key=True)
name:Mapped[str] = db.mapped_column(db.String(80), nullable=False, unique=True)
price:Mapped[int] = db.mapped_column(db.Integer, nullable=False)
brand_id:Mapped[int]= db.mapped_column(db.Integer, db.ForeignKey('brand.id'), nullable=False)
brand:Mapped['Brand'] = db.relationship(back_populates='product')
image_mime:Mapped[str] = db.mapped_column(db.String)
image_data:Mapped[bytes] = db.mapped_column(db.LargeBinary)
@app.route('/addproduct', methods=['POST','GET'])
def addproduct():
form = AddProductForm()
if form.validate_on_submit():
product = Product()
form.populate_obj(product)
product.image_mime = form.image_1.data.mimetype
product.image_data = form.image_1.data.read()
db.session.add(product)
db.session.commit()
flash(f'The product {product.name} has been added.')
return redirect(url_for('addproduct'))
return render_template('products/addproduct.html', **locals())
@app.route('/image/<int:product_id>')
def download_image(product_id):
product = db.get_or_404(Product, product_id)
return send_file(
io.BytesIO(product.image_data),
mimetype=product.image_mime
)