0

I have created two models in flask-sqlalchemy Product and Brand. Both have a relationship to each other. Whenever I try to create a brand I get the error AttributeError: 'tuple' object has no attribute 'items'.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
from flask_admin import Admin
from flask_admin.contrib.sqla import ModelView


app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql+psycopg2://postgres:postgres@localhost:5432/ecommerce'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SECRET_KEY'] = 'secret'
db = SQLAlchemy(app)



# Models
class Brand(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100), nullable=False, unique=True)
    products = db.relationship('Product', backref='brand', lazy=True)

    def __str__(self):
        return self.name

def format_products(view, context, model, name):
    return ', '.join([product.name for product in model.products])

class BrandView(ModelView):
    column_list = ['name', 'products']
    column_formatters = {'products': format_products}
    form_excluded_columns = ['products']

-

class Product(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(200), nullable=False)
    description = db.Column(db.Text)
    price = db.Column(db.Float, nullable=False)
    brand_id = db.Column(db.Integer, db.ForeignKey('brand.id'), nullable=False)



with app.app_context():
    db.create_all()

admin = Admin(app)
admin.add_view(ModelView(Product, db.session))
admin.add_view(BrandView(Brand, db.session))


@app.route('/')
def hello_world():
    return 'Hello World!'


if __name__ == '__main__':
    app.run()

I am not able to create a Brand.

1
  • 1
    Please edit the question to include the complete error traceback and the code that tries to create the brand. Commented Jun 11 at 8:46

1 Answer 1

0

The problem is caused by the column name, which is declared as unique. For some time now, the resulting field_flags in wtforms are no longer assumed to be a tuple, but rather a dict. Unfortunately, Flask-Admin still uses the outdated version.

One possible solution is to create and integrate a custom form.

from flask_admin import form
from wtforms import StringField
from wtforms.validators import InputRequired, Length, ValidationError

# ...

class BrandForm(form.BaseForm):
    name = StringField('Name', validators=[
        InputRequired(), 
        Length(max=100)
    ])
    def validate_name(self, field):
        stmt = db.select(Brand).where(Brand.name == field.data)
        if db.session.execute(stmt).scalar(): 
            raise ValidationError('The name is already in use.')

class BrandView(ModelView):
    column_list = ['name', 'products']
    column_formatters = {'products': format_products}
    form = BrandForm

# ...
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.