@@ -174,7 +174,10 @@ def get_field_default(
174174 if isinstance (field , ForeignObjectRel ):
175175 if isinstance (field , ManyToOneRel ):
176176 return Empty , list
177- return Empty , None
177+ return None , None
178+
179+ if isinstance (field , ForeignKey ) and field .null :
180+ return None , None
178181
179182 if isinstance (field , ManyToManyField ):
180183 return Empty , list
@@ -189,6 +192,35 @@ def get_field_default(
189192
190193 return default , default_factory
191194
195+ @classmethod
196+ def get_model_fields (
197+ cls , model_type : type [T ]
198+ ) -> Generator [tuple [str , AnyField ], None , None ]:
199+ for field in model_type ._meta .get_fields ():
200+ yield field .name , field
201+ if isinstance (field , ForeignKey ):
202+ # if it's a fk, also include the implicitly generated '_id' fields
203+ # generated by django
204+ for fk_tuple in field .related_fields :
205+ # 'attname' is the name of the '_id' field on the referring model
206+ name = fk_tuple [0 ].attname
207+ # there is no concrete, distinct field for the '_id' attribute;
208+ # it's the same as the 'ForeignKey' field on the type. on the
209+ # concrete class, these fields are 'ForwardManyToOneDescriptor' for
210+ # the explicitly defined 'ForeignKey' field, and a
211+ # 'ForeignKeyDeferredAttribute' for the implicitly created '_id'
212+ # field.
213+ # we need a concrete field to infer the type though, so we construct
214+ # it from the type of the related primary key field
215+ related_field = fk_tuple [1 ]
216+ id_field = type (related_field )(
217+ name = name ,
218+ null = field .null ,
219+ validators = related_field .validators ,
220+ default = None if field .null else NOT_PROVIDED ,
221+ )
222+ yield name , id_field
223+
192224 @classmethod
193225 def generate_field_definitions (
194226 cls , model_type : Type [T ]
@@ -198,9 +230,7 @@ def generate_field_definitions(
198230 field_type_map = {** _FIELD_TYPE_MAP , ** cls .custom_field_types }
199231
200232 field : AnyField
201- for field in model_type ._meta .get_fields ():
202- name = field .name
203-
233+ for name , field in cls .get_model_fields (model_type ):
204234 if field .hidden :
205235 dto_field = DTOField ("private" )
206236 elif not field .editable :
@@ -210,6 +240,9 @@ def generate_field_definitions(
210240
211241 if field .is_relation and field .related_model :
212242 related = field .related_model
243+ # all relationships are 'read-only', because Django does not support
244+ # inline creation of related objects
245+ dto_field = DTOField ("read-only" )
213246 if isinstance (field , (ForeignKey , OneToOneField )):
214247 field_type : Any = related
215248 elif isinstance (field , ManyToManyField ) or getattr (
0 commit comments