diff --git a/sqladmin/forms.py b/sqladmin/forms.py index 1328e516..00daa7d1 100644 --- a/sqladmin/forms.py +++ b/sqladmin/forms.py @@ -18,7 +18,12 @@ import anyio from sqlalchemy import Boolean, select from sqlalchemy import inspect as sqlalchemy_inspect -from sqlalchemy.orm import ColumnProperty, RelationshipProperty, sessionmaker +from sqlalchemy.orm import ( + ColumnProperty, + RelationshipProperty, + sessionmaker, +) +from sqlalchemy.sql.elements import Label from sqlalchemy.sql.schema import Column from wtforms import ( BooleanField, @@ -612,9 +617,12 @@ async def get_model_form( attributes = [] names = only or mapper.attrs.keys() for name in names: - if exclude and name in exclude: + attr = mapper.attrs[name] + if (exclude and name in exclude) or ( + isinstance(attr, ColumnProperty) and isinstance(attr.expression, Label) + ): continue - attributes.append((name, mapper.attrs[name])) + attributes.append((name, attr)) field_dict = {} for name, attr in attributes: diff --git a/tests/test_forms/test_forms.py b/tests/test_forms/test_forms.py index a3faf48e..b8fd9529 100644 --- a/tests/test_forms/test_forms.py +++ b/tests/test_forms/test_forms.py @@ -16,11 +16,14 @@ Text, Time, TypeDecorator, + func, + select, ) from sqlalchemy.dialects.postgresql import ARRAY, INET, MACADDR, UUID from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import ( ColumnProperty, + column_property, composite, declarative_base, relationship, @@ -333,3 +336,16 @@ class DataModel(Base): assert inspect.isfunction(Form.process) assert inspect.isfunction(Form.validate) assert inspect.isfunction(Form.populate_obj) + + +async def test_column_property_is_ignored_in_form() -> None: + class Model(Base): + __tablename__ = "model_column_property" + + id = Column(Integer, primary_key=True) + number = Column(Integer) + count = column_property(select(func.count("Model")).scalar_subquery()) + + Form = await get_model_form(model=Model, session_maker=session_maker) + + assert "count" not in Form()._fields