@@ -334,3 +334,115 @@ async def test_multiple_files_multiple():
334334
335335 m = await fastui_form (FormMultipleFiles ).dependency (request )
336336 assert m .model_dump () == {'files' : [file1 , file2 ]}
337+
338+
339+ class FixedTuple (BaseModel ):
340+ foo : Tuple [str , int , int ]
341+
342+
343+ def test_fixed_tuple ():
344+ m = components .ModelForm (model = FixedTuple , submit_url = '/foo/' )
345+ # insert_assert(m.model_dump(by_alias=True, exclude_none=True))
346+ assert m .model_dump (by_alias = True , exclude_none = True ) == {
347+ 'submitUrl' : '/foo/' ,
348+ 'method' : 'POST' ,
349+ 'type' : 'ModelForm' ,
350+ 'formFields' : [
351+ {
352+ 'name' : 'foo.0' ,
353+ 'title' : ['Foo' , '0' ],
354+ 'required' : True ,
355+ 'locked' : False ,
356+ 'htmlType' : 'text' ,
357+ 'type' : 'FormFieldInput' ,
358+ },
359+ {
360+ 'name' : 'foo.1' ,
361+ 'title' : ['Foo' , '1' ],
362+ 'required' : True ,
363+ 'locked' : False ,
364+ 'htmlType' : 'number' ,
365+ 'type' : 'FormFieldInput' ,
366+ },
367+ {
368+ 'name' : 'foo.2' ,
369+ 'title' : ['Foo' , '2' ],
370+ 'required' : True ,
371+ 'locked' : False ,
372+ 'htmlType' : 'number' ,
373+ 'type' : 'FormFieldInput' ,
374+ },
375+ ],
376+ }
377+
378+
379+ async def test_fixed_tuple_submit ():
380+ request = FakeRequest ([('foo.0' , 'bar' ), ('foo.1' , '123' ), ('foo.2' , '456' )])
381+
382+ m = await fastui_form (FixedTuple ).dependency (request )
383+ assert m .model_dump () == {'foo' : ('bar' , 123 , 456 )}
384+
385+
386+ class NestedTuple (BaseModel ):
387+ bar : FixedTuple
388+
389+
390+ def test_fixed_tuple_nested ():
391+ m = components .ModelForm (model = NestedTuple , submit_url = '/foobar/' )
392+ # insert_assert(m.model_dump(by_alias=True, exclude_none=True))
393+ assert m .model_dump (by_alias = True , exclude_none = True ) == {
394+ 'submitUrl' : '/foobar/' ,
395+ 'method' : 'POST' ,
396+ 'type' : 'ModelForm' ,
397+ 'formFields' : [
398+ {
399+ 'name' : 'bar.foo.0' ,
400+ 'title' : ['FixedTuple' , 'Foo' , '0' ],
401+ 'required' : True ,
402+ 'locked' : False ,
403+ 'htmlType' : 'text' ,
404+ 'type' : 'FormFieldInput' ,
405+ },
406+ {
407+ 'name' : 'bar.foo.1' ,
408+ 'title' : ['FixedTuple' , 'Foo' , '1' ],
409+ 'required' : True ,
410+ 'locked' : False ,
411+ 'htmlType' : 'number' ,
412+ 'type' : 'FormFieldInput' ,
413+ },
414+ {
415+ 'name' : 'bar.foo.2' ,
416+ 'title' : ['FixedTuple' , 'Foo' , '2' ],
417+ 'required' : True ,
418+ 'locked' : False ,
419+ 'htmlType' : 'number' ,
420+ 'type' : 'FormFieldInput' ,
421+ },
422+ ],
423+ }
424+
425+
426+ async def test_fixed_tuple_nested_submit ():
427+ request = FakeRequest ([('bar.foo.0' , 'bar' ), ('bar.foo.1' , '123' ), ('bar.foo.2' , '456' )])
428+
429+ m = await fastui_form (NestedTuple ).dependency (request )
430+ assert m .model_dump () == {'bar' : {'foo' : ('bar' , 123 , 456 )}}
431+
432+
433+ def test_variable_tuple ():
434+ class VarTuple (BaseModel ):
435+ foo : Tuple [str , ...]
436+
437+ m = components .ModelForm (model = VarTuple , submit_url = '/foo/' )
438+ with pytest .raises (NotImplementedError , match = 'Array fields are not fully supported' ):
439+ m .model_dump (by_alias = True , exclude_none = True )
440+
441+
442+ def test_tuple_optional ():
443+ class TupleOptional (BaseModel ):
444+ foo : Tuple [str , Union [str , None ]]
445+
446+ m = components .ModelForm (model = TupleOptional , submit_url = '/foo/' )
447+ with pytest .raises (NotImplementedError , match = 'Tuples with optional fields are not yet supported' ):
448+ m .model_dump (by_alias = True , exclude_none = True )
0 commit comments