From 32ae1c9e504a07b6a74690daafccf034e11f4358 Mon Sep 17 00:00:00 2001 From: Himalaya Date: Fri, 12 Apr 2024 19:05:02 +0530 Subject: [PATCH 1/2] Add functionality to convert JSON schema to Django models --- src/components/EditorHeader/ControlPanel.jsx | 16 +++++ src/utils/toSQL.js | 65 ++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index 9d064b68..ed1602fa 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -30,6 +30,7 @@ import { jsonToSQLite, jsonToMariaDB, jsonToSQLServer, + jsonToDjangoModels } from "../../utils/toSQL"; import { ObjectType, @@ -933,6 +934,21 @@ export default function ControlPanel({ })); }, }, + { + DjangoModels: () => { + setModal(MODAL.CODE); + const src = jsonToDjangoModels({ + tables: tables, + references: relationships, + types: types, + }); + setExportData((prev) => ({ + ...prev, + data: src, + extension: "sql", + })); + }, + }, ], function: () => {}, }, diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 4720a6ef..19502bc6 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -397,6 +397,71 @@ export function jsonToSQLite(obj) { .join("\n"); } +export function jsonToDjangoModels(obj) { + let djangoCode = ""; + + obj.types.forEach(type => { + let typeStatements = []; + + type.fields.forEach(field => { + if (field.type === "ENUM" || field.type === "SET") { + typeStatements.push(` ${field.name}_t = models.TextChoices(\n${field.values.map(value => ` ('${value}', '${value}')`).join(',\n')}\n )`); + } + }); + + // Generate Django model definition + djangoCode += `class ${type.name}(models.Model) {\n`; + type.fields.forEach(field => { + djangoCode += ` ${field.name} = models.${getTypeString(field)}(`; + if (field.primary) { + djangoCode += "primary_key=True, "; + } + if (field.unique) { + djangoCode += "unique=True, "; + } + if (!field.notNull) { + djangoCode += "null=True, "; + } + if (field.default !== "") { + djangoCode += `default=${parseDefault(field)}, `; + } + djangoCode += ")\n"; + }); + djangoCode += `}\n\n`; + }); + + obj.tables.forEach(table => { + djangoCode += `class ${table.name}(models.Model) {\n`; + table.fields.forEach(field => { + djangoCode += ` ${field.name} = models.${getTypeString(field)}(`; + if (field.primary) { + djangoCode += "primary_key=True, "; + } + if (field.unique) { + djangoCode += "unique=True, "; + } + if (!field.notNull) { + djangoCode += "null=True, "; + } + if (field.default !== "") { + djangoCode += `default=${parseDefault(field)}, `; + } + djangoCode += ")\n"; + }); + djangoCode += `}\n\n`; + }); + + obj.references.forEach(reference => { + const startTable = obj.tables[reference.startTableId]; + const endTable = obj.tables[reference.endTableId]; + djangoCode += `class ${startTable.name}(models.Model) {\n`; + djangoCode += ` ${endTable.name} = models.ForeignKey('${endTable.name}', on_delete=models.${reference.deleteConstraint}, related_name='${startTable.name.toLowerCase()}_${endTable.name.toLowerCase()}', db_column='${endTable.name.toLowerCase()}')\n`; + djangoCode += `\n`; + }); + + return djangoCode; +} + export function jsonToMariaDB(obj) { return `${obj.tables .map( From ee14515971ca02729f1bebe886db4f280c63bd17 Mon Sep 17 00:00:00 2001 From: Himalaya Date: Mon, 15 Apr 2024 22:48:26 +0530 Subject: [PATCH 2/2] Added data type mapping from sql to python django models --- src/components/EditorHeader/ControlPanel.jsx | 6 +-- src/data/constants.js | 41 +++++++++++++++++++- src/utils/toSQL.js | 20 +++++++--- 3 files changed, 57 insertions(+), 10 deletions(-) diff --git a/src/components/EditorHeader/ControlPanel.jsx b/src/components/EditorHeader/ControlPanel.jsx index ed1602fa..2cc5c5f1 100644 --- a/src/components/EditorHeader/ControlPanel.jsx +++ b/src/components/EditorHeader/ControlPanel.jsx @@ -935,7 +935,7 @@ export default function ControlPanel({ }, }, { - DjangoModels: () => { + "Django Models": () => { setModal(MODAL.CODE); const src = jsonToDjangoModels({ tables: tables, @@ -945,10 +945,10 @@ export default function ControlPanel({ setExportData((prev) => ({ ...prev, data: src, - extension: "sql", + extension: "py", })); }, - }, + } ], function: () => {}, }, diff --git a/src/data/constants.js b/src/data/constants.js index 8d8d1e1a..415bbb2e 100644 --- a/src/data/constants.js +++ b/src/data/constants.js @@ -125,4 +125,43 @@ export const SIDESHEET = { NONE: 0, TODO: 1, TIMELINE: 2, -}; \ No newline at end of file +}; + +export const SQL_TO_DJANGO_TYPE_MAPPING = { + 'INT': 'IntegerField', + 'TINYINT': 'SmallIntegerField', + 'SMALLINT': 'SmallIntegerField', + 'MEDIUMINT': 'IntegerField', + 'BIGINT': 'BigIntegerField', + 'FLOAT': 'FloatField', + 'DOUBLE': 'FloatField', + 'DECIMAL': 'DecimalField', + 'DATE': 'DateField', + 'TIME': 'TimeField', + 'DATETIME': 'DateTimeField', + 'TIMESTAMP': 'DateTimeField', + 'YEAR': 'IntegerField', + 'CHAR': 'CharField', + 'VARCHAR': 'CharField', + 'TEXT': 'TextField', + 'BLOB': 'BinaryField', + 'MEDIUMBLOB': 'BinaryField', + 'LONGBLOB': 'BinaryField', + 'TINYBLOB': 'BinaryField', +}; + +export const SQL_TO_DJANGO_DELETE_CONSTRAINT_MAPPING = { + 'NO ACTION': 'DO_NOTHING', + 'RESTRICT': 'PROTECT', + 'CASCADE': 'CASCADE', + 'SET NULL': 'SET_NULL', + 'SET DEFAULT': 'SET_DEFAULT', +}; + +export const SQL_TO_DJANGO_UPDATE_CONSTRAINT_MAPPING = { + 'NO ACTION': 'DO_NOTHING', + 'RESTRICT': 'PROTECT', + 'CASCADE': 'CASCADE', + 'SET NULL': 'SET_NULL', + 'SET DEFAULT': 'SET_DEFAULT', +}; diff --git a/src/utils/toSQL.js b/src/utils/toSQL.js index 19502bc6..5a1db760 100644 --- a/src/utils/toSQL.js +++ b/src/utils/toSQL.js @@ -1,6 +1,8 @@ -import { sqlDataTypes } from "../data/constants"; +import { sqlDataTypes, SQL_TO_DJANGO_TYPE_MAPPING, + SQL_TO_DJANGO_DELETE_CONSTRAINT_MAPPING, + SQL_TO_DJANGO_UPDATE_CONSTRAINT_MAPPING } + from "../data/constants"; import { strHasQuotes } from "./utils"; - export function getJsonType(f) { if (!sqlDataTypes.includes(f.type)) { return '{ "type" : "object", additionalProperties : true }'; @@ -400,6 +402,9 @@ export function jsonToSQLite(obj) { export function jsonToDjangoModels(obj) { let djangoCode = ""; + djangoCode += "from django.db import models\n"; + djangoCode += "from django.db.models import DO_NOTHING, CASCADE, PROTECT, SET_NULL, SET_DEFAULT\n\n"; + obj.types.forEach(type => { let typeStatements = []; @@ -409,10 +414,10 @@ export function jsonToDjangoModels(obj) { } }); - // Generate Django model definition djangoCode += `class ${type.name}(models.Model) {\n`; type.fields.forEach(field => { - djangoCode += ` ${field.name} = models.${getTypeString(field)}(`; + const djangoType = SQL_TO_DJANGO_TYPE_MAPPING[field.type] || 'CharField'; + djangoCode += ` ${field.name} = models.${djangoType}(`; if (field.primary) { djangoCode += "primary_key=True, "; } @@ -433,7 +438,8 @@ export function jsonToDjangoModels(obj) { obj.tables.forEach(table => { djangoCode += `class ${table.name}(models.Model) {\n`; table.fields.forEach(field => { - djangoCode += ` ${field.name} = models.${getTypeString(field)}(`; + const djangoType = SQL_TO_DJANGO_TYPE_MAPPING[field.type] || 'CharField'; // Convert SQL type to Django type + djangoCode += ` ${field.name} = models.${djangoType}(`; if (field.primary) { djangoCode += "primary_key=True, "; } @@ -454,8 +460,10 @@ export function jsonToDjangoModels(obj) { obj.references.forEach(reference => { const startTable = obj.tables[reference.startTableId]; const endTable = obj.tables[reference.endTableId]; + const deleteConstraint = SQL_TO_DJANGO_DELETE_CONSTRAINT_MAPPING[reference.deleteConstraint.toUpperCase()] || 'CASCADE'; + const updateConstraint = SQL_TO_DJANGO_UPDATE_CONSTRAINT_MAPPING[reference.updateConstraint.toUpperCase()] || 'CASCADE'; djangoCode += `class ${startTable.name}(models.Model) {\n`; - djangoCode += ` ${endTable.name} = models.ForeignKey('${endTable.name}', on_delete=models.${reference.deleteConstraint}, related_name='${startTable.name.toLowerCase()}_${endTable.name.toLowerCase()}', db_column='${endTable.name.toLowerCase()}')\n`; + djangoCode += ` ${endTable.name} = models.ForeignKey('${endTable.name}', on_delete=models.${deleteConstraint}, related_name='${startTable.name.toLowerCase()}_${endTable.name.toLowerCase()}', db_column='${endTable.name.toLowerCase()}', on_update=models.${updateConstraint})\n`; djangoCode += `\n`; });