Skip to content

Commit 50e19a9

Browse files
authored
Fixed the StopIteration exception raised in Sqlalchemy ModelView when the primary key is not included in the field list. (#482)
* fix #481 * fix ci * add changelog
1 parent 63948e3 commit 50e19a9

File tree

4 files changed

+30
-11
lines changed

4 files changed

+30
-11
lines changed

docs/changelog/index.md

+3
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
99

10+
* Fixed the StopIteration exception raised in Sqlalchemy ModelView when the primary key is not included in the field
11+
list by [@jowilf](https://github.com/jowilf) in [#482](https://github.com/jowilf/starlette-admin/pull/482)
12+
1013
## [0.13.0] - 2023-01-16
1114

1215
### Added

starlette_admin/contrib/sqla/view.py

+9-8
Original file line numberDiff line numberDiff line change
@@ -107,13 +107,10 @@ def __init__(
107107
for f in self.model.__dict__
108108
if type(self.model.__dict__[f]) is InstrumentedAttribute
109109
]
110-
self._setup_primary_key()
111110
self.fields = (converter or ModelConverter()).convert_fields_list(
112111
fields=self.fields, model=self.model, mapper=mapper
113112
)
114-
self.pk_field: BaseField = next(
115-
f for f in self.fields if f.name == self.pk_attr
116-
)
113+
self._setup_primary_key()
117114
self.exclude_fields_from_list = normalize_list(self.exclude_fields_from_list) # type: ignore
118115
self.exclude_fields_from_detail = normalize_list(self.exclude_fields_from_detail) # type: ignore
119116
self.exclude_fields_from_create = normalize_list(self.exclude_fields_from_create) # type: ignore
@@ -157,16 +154,20 @@ def _setup_primary_key(self) -> None:
157154
self._pk_coerce = tuple(
158155
extract_column_python_type(c) for c in self._pk_column
159156
)
160-
pk_field = MultiplePKField(_pk_attrs)
161-
self.pk_attr = pk_field.name
162-
self.fields.append(pk_field) # type: ignore[attr-defined]
157+
self.pk_field: BaseField = MultiplePKField(_pk_attrs)
163158
else:
164159
assert (
165160
len(_pk_attrs) == 1
166161
), f"No primary key found in model {self.model.__name__}"
167-
self.pk_attr = _pk_attrs[0]
168162
self._pk_column = getattr(self.model, _pk_attrs[0])
169163
self._pk_coerce = extract_column_python_type(self._pk_column) # type: ignore[arg-type]
164+
try:
165+
# Try to find the primary key field among the fields
166+
self.pk_field = next(f for f in self.fields if f.name == _pk_attrs[0])
167+
except StopIteration:
168+
# If the primary key is not among the fields, treat its value as a string
169+
self.pk_field = StringField(_pk_attrs[0])
170+
self.pk_attr = self.pk_field.name
170171

171172
async def handle_action(
172173
self, request: Request, pks: List[Any], name: str

tests/sqla/test_multiple_pks.py

+1-3
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,9 @@ async def client(app):
6969
yield c
7070

7171

72-
async def test_model_view(session: Session):
72+
async def test_pk_field(session: Session):
7373
view = ModelView(Record)
7474

75-
assert len(view.fields) == 5
76-
assert view.pk_field == view.fields[-1]
7775
assert isinstance(view.pk_field, MultiplePKField)
7876
assert view.pk_field.name == "id1,id2,id3"
7977

tests/sqla/test_view_meta.py

+17
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,23 @@ def test_other_fields_conversion():
202202
]
203203

204204

205+
def test_pk_field():
206+
assert ModelView(Document).pk_field == IntegerField(
207+
"int",
208+
required=True,
209+
exclude_from_create=True,
210+
exclude_from_edit=True,
211+
help_text="This is the primary key",
212+
)
213+
214+
215+
def test_pk_field_excluded_from_fields():
216+
class DocumentView(ModelView):
217+
fields = ["float"]
218+
219+
assert DocumentView(Document).pk_field == StringField("int")
220+
221+
205222
def test_not_supported_array_columns():
206223
with pytest.raises(
207224
NotSupportedColumn, match="Column ARRAY with dimensions != 1 is not supported"

0 commit comments

Comments
 (0)