Skip to content

Commit

Permalink
Add forgeinkey support
Browse files Browse the repository at this point in the history
  • Loading branch information
enzofrnt committed Mar 10, 2024
1 parent cf53713 commit 66f5c90
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 52 deletions.
1 change: 1 addition & 0 deletions django_model_to_typescript_types/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def ready(self):
if os.environ.get('RUN_MAIN') :
print('Lets convert the models to typescript types')
# converter = ModelToTypeScriptConverter(os.environ.get('TS_APP_TO_INCLUDE', 'app'), os.environ.get('TS_PATH', '/tmp/tsinterface/interfaces.ts'), os.environ.get('TS_SEPERATED_FILES', False))
print("TS_SEPERATED_FILES "+os.environ.get('TS_SEPERATED_FILES', True))
converter = ModelToTypeScriptConverter(os.environ.get('TS_APP_TO_INCLUDE', 'app'), os.environ.get('TS_PATH', '/tmp/tsinterface/'), os.environ.get('TS_SEPERATED_FILES', True))
converter.generate_interfaces()

Expand Down
121 changes: 69 additions & 52 deletions django_model_to_typescript_types/modeltotypescriptconverter.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ class ModelToTypeScriptConverter:
def __init__(self, apps_to_include=['app'], path_for_interfaces='/tmp/tsinterface/', seperated_files=False):
self.apps_to_include = apps_to_include.split(',')
self.path_for_interfaces = path_for_interfaces
self.seperated_files = seperated_files
if isinstance(seperated_files, str):
self.seperated_files = seperated_files.lower() in ('true')
else:
self.seperated_files = bool(seperated_files)
self.field_type_mapping = {
"AutoField": "number",
"BooleanField": "boolean",
Expand All @@ -18,7 +21,7 @@ def __init__(self, apps_to_include=['app'], path_for_interfaces='/tmp/tsinterfac
"DateTimeField": "Date",
"DecimalField": "number",
"FloatField": "number",
"ForeignKey": "number",
# "ForeignKey": "number", The foreign key is handled separately
"IntegerField": "number",
"JSONField": "JSON",
"ManyToManyField": None,
Expand All @@ -31,7 +34,6 @@ def __init__(self, apps_to_include=['app'], path_for_interfaces='/tmp/tsinterfac
}

def get_wsgi_application(self):
# Get the WSGI application
get_wsgi_application()

def to_camel_case(self, snake_str):
Expand All @@ -45,57 +47,72 @@ def to_type_union(self, field):
return " | ".join([f'"{choice[0]}"' for choice in choices])

def generate_interfaces(self):
# self.get_wsgi_application()
all_models = apps.get_models()

os.makedirs(os.path.dirname(self.path_for_interfaces), exist_ok=True)

if self.seperated_files:
for model in all_models:
if model._meta.app_label not in self.apps_to_include:
continue

with open(self.path_for_interfaces + model.__name__ + ".ts", "w") as file:
print(f"Generating {model.__name__}.ts")
file.write(f"export interface {model.__name__}{' {'}\n")

for field in model._meta.fields:
_type = self.field_type_mapping.get(field.get_internal_type(), None)
if _type is None:
continue

if field.choices:
_type = self.to_type_union(field)

name = self.to_camel_case(field.name)

# If the field allows null values we add the ? to the type.
if field.null:
name += "?"

file.write(f"\t{name}: {_type};\n")
file.write("}\n\n")
else :
with open(self.path_for_interfaces + "interfaces.ts", "w") as file:
print("Generating interfaces.ts")
for model in all_models:
if model._meta.app_label not in self.apps_to_include:
continue

file.write(f"export interface {model.__name__}{' {'}\n")

for field in model._meta.fields:
_type = self.field_type_mapping.get(field.get_internal_type(), None)
if _type is None:
continue

if field.choices:
_type = self.to_type_union(field)

name = self.to_camel_case(field.name)

# If the field allows null values we add the ? to the type.
if field.null:
name += "?"
if model._meta.app_label in self.apps_to_include:
self.generate_interface_file(model)
else:
self.generate_single_interface_file(all_models)

def generate_field_line(self, field):
if field.get_internal_type() == 'ForeignKey':
# Handling ForeignKey specifically
related_model = field.related_model
print("related name of the foreign key : "+field.remote_field.related_name or f'{related_model._meta.model_name}_set')
# You could choose to reference the primary key type of the related model
# or simply use the related model's name as a type
_type = related_model.__name__ + " | " + self.field_type_mapping.get(related_model._meta.pk.get_internal_type(), None)
# Assuming we always import the related model's type at the top of the file
needs_import = True
else:
_type = self.field_type_mapping.get(field.get_internal_type(), None)
needs_import = False

if _type is None:
return None, needs_import

if field.choices:
_type = self.to_type_union(field)

name = self.to_camel_case(field.name)
if field.null:
name += "?"

return f"{name}: {_type};", needs_import

def generate_interface_definition(self, model):
lines = [f"export interface {model.__name__}{{\n"]
imports_needed = set()
for field in model._meta.fields:
field_line, needs_import = self.generate_field_line(field)
if field_line:
lines.append(f"\t{field_line}\n")
if needs_import and self.seperated_files :
related_model = field.related_model
imports_needed.add(related_model.__name__)
header = ""
if imports_needed:
# Generate import lines for needed models
for import_model in imports_needed:
header += f"import {{ {import_model} }} from './{import_model}';\n"
header += "\n" # Add a newline after imports for readability
lines.insert(0, header)
lines.append("}\n\n")
return "".join(lines)

def generate_interface_file(self, model):
with open(f"{self.path_for_interfaces}{model.__name__.lower()}.ts", "w") as file:
print(f"Generating {model.__name__}.ts")
file.write(self.generate_interface_definition(model))

def generate_single_interface_file(self, all_models):
with open(f"{self.path_for_interfaces}interfaces.ts", "w") as file:
print("Generating interfaces.ts")
for model in all_models:
if model._meta.app_label in self.apps_to_include:
file.write(self.generate_interface_definition(model))

file.write(f"\t{name}: {_type};\n")
file.write("}\n\n")

0 comments on commit 66f5c90

Please sign in to comment.