File tree Expand file tree Collapse file tree
Expand file tree Collapse file tree Original file line number Diff line number Diff line change 2121 - name : Setup Node.js
2222 uses : actions/setup-node@v3
2323 with :
24- node-version : v22.4.1
24+ node-version : v24.3.0
2525 cache : ' yarn'
2626 cache-dependency-path : ' frontend/yarn.lock'
2727 - name : Install Dependencies
Original file line number Diff line number Diff line change @@ -42,6 +42,12 @@ def read_cls_docstring(cls):
4242
4343def get_versions ():
4444 return [
45+ {
46+ "version" : "0.3.1" ,
47+ "changes" : [
48+ "Fix sqlalchemy required fields. Fix CI." ,
49+ ],
50+ },
4551 {
4652 "version" : "0.3.0" ,
4753 "changes" : [
Original file line number Diff line number Diff line change @@ -70,11 +70,14 @@ def get_model_fields_with_widget_types(
7070 is_immutable = (
7171 is_pk or bool (getattr (orm_model_field , "onupdate" , None ))
7272 ) and field_name not in self .readonly_fields
73- required = (
74- not getattr (orm_model_field , "nullable" , False )
75- and not getattr (orm_model_field , "default" , False )
76- and not is_m2m
77- )
73+ # For columns, use the column's nullable; for relationships (MANYTOONE/ONETOONE),
74+ # the relationship object has no nullable — use the underlying FK column's nullable.
75+ nullable = getattr (orm_model_field , "nullable" , False )
76+ if field_type in ("ONETOONE" , "MANYTOONE" ):
77+ fk_column = next ((c for c in mapper .c if c .key == column_name ), None )
78+ if fk_column is not None :
79+ nullable = getattr (fk_column , "nullable" , False )
80+ required = not nullable and not getattr (orm_model_field , "default" , False ) and not is_m2m
7881 choices = (
7982 orm_model_field .type ._object_lookup
8083 if hasattr (orm_model_field , "type" ) and hasattr (orm_model_field .type , "_object_lookup" )
Original file line number Diff line number Diff line change @@ -322,3 +322,22 @@ def test_get_form_widget_event(event):
322322 assert field .form_widget_props
323323 case _:
324324 raise ValueError (f"Unexpected field: { field .name } " )
325+
326+
327+ def test_nullable_foreign_key_form_required (event ):
328+ """Nullable FK (base) must have required=False; non-nullable FK (tournament) required=True.
329+
330+ Suitable for all ORMs: Django/Tortoise use field.null, Pony uses is_required,
331+ SQLAlchemy uses the underlying FK column's nullable.
332+ """
333+ admin_model = get_admin_model (event .__class__ )
334+ fields = admin_model .get_model_fields_with_widget_types ()
335+ by_name = {f .name : f for f in fields }
336+ assert "base" in by_name , "Event should have base (nullable FK) field"
337+ assert "tournament" in by_name , "Event should have tournament (required FK) field"
338+ assert (
339+ by_name ["base" ].form_widget_props ["required" ] is False
340+ ), "nullable FK base_id should be optional in admin form"
341+ assert (
342+ by_name ["tournament" ].form_widget_props ["required" ] is True
343+ ), "non-nullable FK tournament_id should be required in admin form"
You can’t perform that action at this time.
0 commit comments