feat(djinit): introduce base service abstractions and refactor template engine

Add foundational class hierarchy and refactor the template engine to use inheritance and common utilities, enhancing extensibility and code organization.

- New Feature: Added BaseObject, BaseService, BaseCommand, and BaseUtils classes in djinit/core/base.py providing common interfaces.
- Enhancement: Refactored TemplateEngine to inherit BaseService and utilize BaseUtils for logic parsing.
- Enhancement: Updated InFileLogicParser to enforce proper stack handling and simplified conditional block processing.
- Enhancement: Consolidated utility functions via CommonUtils in generator and ProjectManager, streamlining app configuration upgrades.
This commit is contained in:
sankalp committed 2026-01-31 02:48:13 +05:45
1 parent 64edec1cc2
commit e5f05681d1
7 files changed
+201 -130

No files matched your search

+52
View File
@@ -0,0 +1,52 @@
"""
Base classes for the djinit architecture.
Defines the common interface for services, commands, and utilities.
"""
from abc import abstractmethod
from typing import Any
class BaseObject:
"""
Base object for all djinit classes.
"""
def __init__(self, *args, **kwargs):
pass
class BaseService(BaseObject):
"""
Base class for all services.
Services handle business logic and orchestration.
"""
def __init__(self, project_root: str = None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.project_root = project_root
class BaseCommand(BaseObject):
"""
Base class for all CLI commands.
Commands handle user input and invoke services.
"""
def __init__(self, args: Any, *args_list, **kwargs):
super().__init__(*args_list, **kwargs)
self.args = args
@abstractmethod
def execute(self) -> None:
"""Execute the command."""
pass
class BaseUtils(BaseObject):
"""
Base class for utility collections.
Utilities provide helper functions.
"""
pass
+9 -9
View File
@@ -33,7 +33,7 @@ class InFileLogicParser:
# Stack stores boolean results of nested IF blocks
# Each element is (current_block_result, has_any_true_branch_executed)
stack: List[List[bool]] = []
lines = template_text.splitlines()
i = 0
while i < len(lines):
@@ -57,10 +57,10 @@ class InFileLogicParser:
final_lines.append(line)
i += 1
continue
expr = stripped[9:].strip()
current_stack = stack[-1]
if current_stack[1]:
current_stack[0] = False
else:
@@ -80,7 +80,7 @@ class InFileLogicParser:
final_lines.append(line)
i += 1
continue
current_stack = stack[-1]
if current_stack[1]:
current_stack[0] = False
@@ -121,7 +121,7 @@ class InFileLogicParser:
iterable = eval(iterable_name, {"__builtins__": {}}, self.context)
except Exception:
iterable = []
# Capture loop body
loop_body = []
i += 1
@@ -131,12 +131,12 @@ class InFileLogicParser:
loop_depth += 1
elif lines[i].strip().startswith("# @ENDLOOP"):
loop_depth -= 1
if loop_depth > 0:
loop_body.append(lines[i])
i += 1
if i < len(lines): # Skip the @ENDLOOP line itself
if i < len(lines): # Skip the @ENDLOOP line itself
i += 1
# Execute loop
@@ -181,7 +181,7 @@ class InFileLogicParser:
parts = rendered_line.split("# @IF ", 1)
pre_content = parts[0].rstrip()
rest = parts[1].strip()
# Split rest into expression and post-content (if any)
# This is tricky because the expression might have spaces.
# If there's an # @ENDIF on the same line, use it.
+5 -3
View File
@@ -8,7 +8,9 @@ import subprocess
import sys
from djinit.cli import Cli
from djinit.commands.handler import handle_app_command, handle_secret_command, parse_arguments
from djinit.commands.app import AppCommand
from djinit.commands.handler import parse_arguments
from djinit.commands.secret import SecretCommand
from djinit.ui.console import UIFormatter, console
from djinit.ui.input import confirm_setup, get_user_input
@@ -27,11 +29,11 @@ def main() -> None:
args = parse_arguments()
if args.command == "secret":
handle_secret_command(args)
SecretCommand(args).execute()
return
if args.command == "app":
handle_app_command(args)
AppCommand(args).execute()
return
clear_screen()
+38 -41
View File
@@ -6,29 +6,19 @@ Handles creation of Django apps and updating settings.
import os
from typing import Optional
from djinit.core.base import BaseService
from djinit.ui.console import UIFormatter
from djinit.utils.common import (
calculate_app_module_path,
create_directory_with_init,
create_file_from_template,
create_init_file,
detect_nested_structure_from_settings,
extract_existing_apps,
find_project_dir,
find_settings_path,
get_djinit_config,
insert_apps_into_user_defined_apps,
is_django_project,
)
from djinit.utils.common import CommonUtils
from djinit.utils.django import DjangoHelper
class AppManager:
class AppManager(BaseService):
def __init__(self, app_name: str):
super().__init__()
self.app_name = app_name
self.current_dir = os.getcwd()
self.manage_py_path = os.path.join(self.current_dir, "manage.py")
self.config = get_djinit_config(self.current_dir)
self.config = CommonUtils.get_djinit_config(self.current_dir)
self._project_structure_cache = None
def create_app(self) -> bool:
@@ -59,7 +49,7 @@ class AppManager:
return True
def _is_django_project(self) -> bool:
return is_django_project(self.current_dir)
return CommonUtils.is_django_project(self.current_dir)
def _app_exists(self) -> bool:
# For predefined structure, check in apps/ directory
@@ -79,15 +69,15 @@ class AppManager:
return self._create_predefined_app(os.path.join(self.current_dir, "apps"))
is_nested, nested_dir, apps_base_dir = self._get_project_structure()
# Calculate app_module based on project structure
if is_nested and nested_dir:
# Normalize nested_dir (replace / with . for module path)
module_base = nested_dir.replace(os.path.sep, '.')
module_base = nested_dir.replace(os.path.sep, ".")
app_module = f"{module_base}.{self.app_name}"
else:
app_module = self.app_name
success = DjangoHelper.startapp(self.app_name, apps_base_dir, app_module)
if success:
UIFormatter.print_success(f"Created Django app '{self.app_name}' in {apps_base_dir}")
@@ -96,7 +86,7 @@ class AppManager:
return success
def _add_to_installed_apps(self) -> bool:
settings_path = find_settings_path(self.current_dir)
settings_path = CommonUtils.find_settings_path(self.current_dir)
if not settings_path:
UIFormatter.print_error("Could not find Django settings directory")
return False
@@ -113,30 +103,28 @@ class AppManager:
app_module_path = f"apps.{self.app_name}"
else:
is_nested, nested_dir, _ = self._get_project_structure()
app_module_path = calculate_app_module_path(self.app_name, is_nested, nested_dir)
app_module_path = CommonUtils.calculate_app_module_path(self.app_name, is_nested, nested_dir)
# Use full AppConfig path to properly support nested apps
# e.g. apps.users -> apps.users.apps.UsersConfig
app_config_name = self.app_name.title().replace('_', '')
app_config_name = self.app_name.title().replace("_", "")
full_app_config = f"{app_module_path}.apps.{app_config_name}Config"
# We'll check if either the module path or the full config path exists
# but we'll prefer adding the full config path
existing_apps = extract_existing_apps(content)
existing_apps = CommonUtils.extract_existing_apps(content)
# Check if full config is already there
if full_app_config in existing_apps:
UIFormatter.print_success(f"App '{full_app_config}' already configured in USER_DEFINED_APPS")
return True
# Check if legacy module path is there, if so, upgrade it
if app_module_path in existing_apps:
from djinit.utils.common import replace_app_in_user_defined_apps
UIFormatter.print_info(f"Upgrading app configuration from '{app_module_path}' to '{full_app_config}'...")
updated_content = replace_app_in_user_defined_apps(content, app_module_path, full_app_config)
updated_content = CommonUtils.replace_app_in_user_defined_apps(content, app_module_path, full_app_config)
if updated_content:
with open(base_settings_path, "w") as f:
f.write(updated_content)
@@ -150,7 +138,7 @@ class AppManager:
UIFormatter.print_error("Could not find USER_DEFINED_APPS section in base.py")
return False
updated_content = insert_apps_into_user_defined_apps(content, [full_app_config])
updated_content = CommonUtils.insert_apps_into_user_defined_apps(content, [full_app_config])
if not updated_content:
return False
@@ -177,12 +165,12 @@ class AppManager:
self._project_structure_cache = (nested, nested_dir, apps_base_dir)
else:
# Fallback to old detection method
project_dir, settings_base_path = find_project_dir(self.current_dir)
project_dir, settings_base_path = CommonUtils.find_project_dir(self.current_dir)
if project_dir is None:
self._project_structure_cache = (False, None, self.current_dir)
else:
self._project_structure_cache = detect_nested_structure_from_settings(
self._project_structure_cache = CommonUtils.detect_nested_structure_from_settings(
settings_base_path, self.current_dir
)
return self._project_structure_cache
@@ -210,7 +198,7 @@ class AppManager:
return False
# Check for Single Folder structure (models/ and api/ exist in project dir)
project_dir, _ = find_project_dir(self.current_dir)
project_dir, _ = CommonUtils.find_project_dir(self.current_dir)
if project_dir:
has_models = os.path.isdir(os.path.join(project_dir, "models"))
has_api = os.path.isdir(os.path.join(project_dir, "api"))
@@ -223,11 +211,11 @@ class AppManager:
"""Create an app following the predefined nested structure with custom templates."""
app_dir = os.path.join(apps_dir, self.app_name)
os.makedirs(app_dir, exist_ok=True)
create_init_file(app_dir, f"Created apps/{self.app_name}/__init__.py")
CommonUtils.create_init_file(app_dir, f"Created apps/{self.app_name}/__init__.py")
model_class_name = "".join([part.capitalize() for part in self.app_name.split("_")])
app_module = f"apps.{self.app_name}"
app_config_name = self.app_name.title().replace('_', '')
app_config_name = self.app_name.title().replace("_", "")
base_context = {
"app_name": self.app_name,
"app_config_name": app_config_name,
@@ -235,7 +223,7 @@ class AppManager:
"model_class_name": model_class_name,
}
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(app_dir, "apps.py"),
"components/apps.py-tpl",
{"app_name": self.app_name},
@@ -251,17 +239,26 @@ class AppManager:
}
for folder, files in subfolders.items():
folder_path = os.path.join(app_dir, folder)
create_directory_with_init(folder_path, f"Created apps/{self.app_name}/{folder}/__init__.py")
CommonUtils.create_directory_with_init(folder_path, f"Created apps/{self.app_name}/{folder}/__init__.py")
for filename, template in files:
file_path = os.path.join(folder_path, filename)
create_file_from_template(
CommonUtils.create_file_from_template(
file_path, template, base_context, f"Created apps/{self.app_name}/{folder}/{filename}"
)
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(app_dir, "urls.py"),
"config/urls.py-tpl",
{"url_type": "drf_router", "app_module": app_module, "view_file": f"{self.app_name}_view", "viewset_name": f"{model_class_name}ViewSet", "router_prefix": "", "basename": self.app_name, "has_viewset": True, "has_include": False},
{
"url_type": "drf_router",
"app_module": app_module,
"view_file": f"{self.app_name}_view",
"viewset_name": f"{model_class_name}ViewSet",
"router_prefix": "",
"basename": self.app_name,
"has_viewset": True,
"has_include": False,
},
f"Created apps/{self.app_name}/urls.py",
)
+64 -45
View File
@@ -5,20 +5,14 @@ Handles creation and modification of project files.
import os
from djinit.core.base import BaseService
from djinit.services.templates import template_engine
from djinit.utils.common import (
calculate_app_module_paths,
change_cwd,
create_directory_with_init,
create_file_from_template,
create_file_with_content,
get_package_name,
)
from djinit.utils.common import CommonUtils
class FileManager:
class FileManager(BaseService):
def __init__(self, project_root: str, project_name: str, app_names: list, metadata: dict):
self.project_root = project_root
super().__init__(project_root=project_root)
self.project_name = project_name
self.app_names = app_names
self.metadata = metadata
@@ -38,9 +32,9 @@ class FileManager:
) -> None:
"""Helper to render template and create file."""
target_dir = base_dir or self.project_root
with change_cwd(target_dir):
with CommonUtils.change_cwd(target_dir):
content = template_engine.render_template(template_path, context)
create_file_with_content(filepath, content, message, should_format=should_format)
CommonUtils.create_file_with_content(filepath, content, message, should_format=should_format)
def _create_files_from_specs(self, base_dir: str, folder_specs: dict, base_context: dict = None) -> None:
"""Helper to create multiple folders and files from a specification dict."""
@@ -48,25 +42,27 @@ class FileManager:
for folder_name, file_templates in folder_specs.items():
folder_path = os.path.join(base_dir, folder_name)
create_directory_with_init(folder_path, f"Created {folder_path}/__init__.py")
CommonUtils.create_directory_with_init(folder_path, f"Created {folder_path}/__init__.py")
for filename, template_path in file_templates:
file_path = os.path.join(folder_path, filename)
create_file_from_template(file_path, template_path, base_context, f"Created {file_path}")
CommonUtils.create_file_from_template(file_path, template_path, base_context, f"Created {file_path}")
def _create_apps_py(self, app_dir: str, app_name: str, message: str = None, should_format: bool = False) -> None:
"""Helper to create apps.py file for an app."""
apps_py_path = os.path.join(app_dir, "apps.py")
context = {"app_name": app_name} if app_name else {}
msg = message or f"Created {os.path.basename(app_dir)}/apps.py"
create_file_from_template(apps_py_path, "components/apps.py-tpl", context, msg, should_format=should_format)
CommonUtils.create_file_from_template(
apps_py_path, "components/apps.py-tpl", context, msg, should_format=should_format
)
def _create_subdirectories_with_init(self, base_dir: str, subdirs: list, prefix: str = "") -> None:
"""Helper to create multiple subdirectories with __init__.py files."""
for subdir in subdirs:
subdir_path = os.path.join(base_dir, subdir)
message = f"Created {prefix}{subdir}/__init__.py" if prefix else f"Created {subdir}/__init__.py"
create_directory_with_init(subdir_path, message)
CommonUtils.create_directory_with_init(subdir_path, message)
def _create_settings_package(self, settings_dir: str, base_context: dict, prefix: str) -> None:
"""Helper to create settings package files (base, development, production)."""
@@ -81,8 +77,8 @@ class FileManager:
transformed_apps = []
for app in base_settings_context["app_names"]:
# Transform to full AppConfig path: module.path -> module.path.apps.ConfigName
short_name = app.split('.')[-1]
config_name = short_name.title().replace('_', '') + 'Config'
short_name = app.split(".")[-1]
config_name = short_name.title().replace("_", "") + "Config"
# If it's the special "apps" container in unified structure, we might want to skip or handle differently
# But assumes all user apps follow standard djinit structure
full_path = f"{app}.apps.{config_name}"
@@ -169,7 +165,7 @@ class FileManager:
def create_pyproject(self, metadata: dict) -> None:
package_name = metadata.get("package_name", "backend")
package_name = get_package_name(package_name)
package_name = CommonUtils.get_package_name(package_name)
context = {
"package_name": package_name,
"project_name": self.project_name,
@@ -184,7 +180,7 @@ class FileManager:
def create_project_urls(self) -> None:
"""Create project urls.py using project_config/urls.py-tpl template (replaces update_project_urls)."""
effective_app_modules = calculate_app_module_paths(self.app_names, self.metadata)
effective_app_modules = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
context = {
"url_type": "project",
"project_name": self.project_name,
@@ -212,10 +208,10 @@ class FileManager:
def create_predefined_structure(self) -> None:
apps_dir = os.path.join(self.project_root, "apps")
create_directory_with_init(apps_dir, "Created apps/__init__.py")
CommonUtils.create_directory_with_init(apps_dir, "Created apps/__init__.py")
users_dir = os.path.join(apps_dir, "users")
create_directory_with_init(users_dir, "Created apps/users/__init__.py")
CommonUtils.create_directory_with_init(users_dir, "Created apps/users/__init__.py")
self._create_apps_py(users_dir, "users")
users_subfolders = {
@@ -228,15 +224,26 @@ class FileManager:
self._create_files_from_specs(users_dir, users_subfolders, {"app_module": "apps.users"})
users_urls_path = os.path.join(users_dir, "urls.py")
create_file_from_template(
CommonUtils.create_file_from_template(
users_urls_path,
"config/urls.py-tpl",
{"url_type": "drf_router", "app_module": "apps.users", "view_file": "user_view", "viewset_name": "UserViewSet", "router_prefix": "users", "basename": "users", "has_viewset": True, "has_app_name": True, "app_name": "users", "has_include": True},
{
"url_type": "drf_router",
"app_module": "apps.users",
"view_file": "user_view",
"viewset_name": "UserViewSet",
"router_prefix": "users",
"basename": "users",
"has_viewset": True,
"has_app_name": True,
"app_name": "users",
"has_include": True,
},
"Created apps/users/urls.py",
)
core_dir = os.path.join(apps_dir, "core")
create_directory_with_init(core_dir, "Created apps/core/__init__.py")
CommonUtils.create_directory_with_init(core_dir, "Created apps/core/__init__.py")
core_subfolders = {
"utils": [("responses.py", "presets/predefined/core/utils/responses.py-tpl")],
@@ -246,22 +253,32 @@ class FileManager:
self._create_files_from_specs(core_dir, core_subfolders, {})
exceptions_path = os.path.join(core_dir, "exceptions.py")
create_file_from_template(
CommonUtils.create_file_from_template(
exceptions_path, "presets/predefined/core/exceptions.py-tpl", {}, "Created apps/core/exceptions.py"
)
api_dir = os.path.join(self.project_root, "api")
create_directory_with_init(api_dir, "Created api/__init__.py")
CommonUtils.create_directory_with_init(api_dir, "Created api/__init__.py")
api_urls_path = os.path.join(api_dir, "urls.py")
create_file_from_template(api_urls_path, "config/urls.py-tpl", {"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "api"}, "Created api/urls.py")
CommonUtils.create_file_from_template(
api_urls_path,
"config/urls.py-tpl",
{"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "api"},
"Created api/urls.py",
)
api_v1_dir = os.path.join(api_dir, "v1")
create_directory_with_init(api_v1_dir, "Created api/v1/__init__.py")
CommonUtils.create_directory_with_init(api_v1_dir, "Created api/v1/__init__.py")
api_v1_urls_path = os.path.join(api_v1_dir, "urls.py")
create_file_from_template(api_v1_urls_path, "config/urls.py-tpl", {"url_type": "api_version", "app_name": "v1", "app_list": ["users"], "app_module": "apps"}, "Created api/v1/urls.py")
CommonUtils.create_file_from_template(
api_v1_urls_path,
"config/urls.py-tpl",
{"url_type": "api_version", "app_name": "v1", "app_list": ["users"], "app_module": "apps"},
"Created api/v1/urls.py",
)
project_urls_path = os.path.join(self.project_configs, "urls.py")
create_file_from_template(
CommonUtils.create_file_from_template(
project_urls_path,
"config/urls.py-tpl",
{"url_type": "project", "api_module": "api", "include_api": True},
@@ -272,10 +289,10 @@ class FileManager:
def create_unified_structure(self) -> None:
# 1. Create 'core' directory (Project Config)
core_dir = os.path.join(self.project_root, "core")
create_directory_with_init(core_dir, "Created core/__init__.py")
CommonUtils.create_directory_with_init(core_dir, "Created core/__init__.py")
settings_dir = os.path.join(core_dir, "settings")
create_directory_with_init(settings_dir, "Created core/settings/__init__.py")
CommonUtils.create_directory_with_init(settings_dir, "Created core/settings/__init__.py")
# Create settings files
base_context = {
@@ -291,7 +308,7 @@ class FileManager:
# 2. Create 'apps' directory (The Main App)
apps_dir = os.path.join(self.project_root, "apps")
create_directory_with_init(apps_dir, "Created apps/__init__.py")
CommonUtils.create_directory_with_init(apps_dir, "Created apps/__init__.py")
# Create apps.py for the 'apps' app
self._create_apps_py(apps_dir, "apps", "Created apps/apps.py", should_format=True)
@@ -302,9 +319,9 @@ class FileManager:
# Create 'api' directory with v1
api_dir = os.path.join(apps_dir, "api")
create_directory_with_init(api_dir, "Created apps/api/__init__.py")
CommonUtils.create_directory_with_init(api_dir, "Created apps/api/__init__.py")
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(api_dir, "urls.py"),
"config/urls.py-tpl",
{"url_type": "api_root", "app_name": "api", "version": "v1", "api_module": "apps.api"},
@@ -313,9 +330,9 @@ class FileManager:
)
api_v1_dir = os.path.join(api_dir, "v1")
create_directory_with_init(api_v1_dir, "Created apps/api/v1/__init__.py")
CommonUtils.create_directory_with_init(api_v1_dir, "Created apps/api/v1/__init__.py")
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(api_v1_dir, "urls.py"),
"config/urls.py-tpl",
{"url_type": "api_version", "app_name": "v1", "app_list": [], "app_module": "apps"},
@@ -325,10 +342,10 @@ class FileManager:
def create_single_structure(self) -> None:
project_dir = os.path.join(self.project_root, self.module_name)
create_directory_with_init(project_dir, f"Created {self.module_name}/__init__.py")
CommonUtils.create_directory_with_init(project_dir, f"Created {self.module_name}/__init__.py")
settings_dir = os.path.join(project_dir, "settings")
create_directory_with_init(settings_dir, f"Created {self.module_name}/settings/__init__.py")
CommonUtils.create_directory_with_init(settings_dir, f"Created {self.module_name}/settings/__init__.py")
# Create settings files
base_context = {
@@ -347,28 +364,28 @@ class FileManager:
components = ["admin", "api", "models", "tests"]
self._create_subdirectories_with_init(project_dir, components, f"{self.module_name}/")
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(project_dir, "admin", "__init__.py"),
"components/admin.py-tpl",
{},
f"Created {self.module_name}/admin/__init__.py",
)
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(project_dir, "models", "__init__.py"),
"components/models.py-tpl",
{},
f"Created {self.module_name}/models/__init__.py",
)
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(project_dir, "api", "README.md"),
"components/api_readme.md-tpl",
{"module_name": self.module_name},
f"Created {self.module_name}/api/README.md",
)
create_file_from_template(
CommonUtils.create_file_from_template(
os.path.join(project_dir, "models", "README.md"),
"components/models_readme.md-tpl",
{"module_name": self.module_name},
@@ -447,4 +464,6 @@ class FileManager:
}
filepath = os.path.join(self.project_root, ".djinit")
create_file_with_content(filepath, json.dumps(config, indent=4), "Created .djinit configuration file")
CommonUtils.create_file_with_content(
filepath, json.dumps(config, indent=4), "Created .djinit configuration file"
)
+28 -30
View File
@@ -5,25 +5,22 @@ Handles creation of Django projects and apps.
import os
from djinit.core.base import BaseService
from djinit.ui.console import UIFormatter
from djinit.utils.common import (
calculate_app_module_paths,
create_directory_with_init,
extract_existing_apps,
get_base_settings_path,
insert_apps_into_user_defined_apps,
read_base_settings,
)
from djinit.utils.common import CommonUtils
from djinit.utils.django import DjangoHelper
class ProjectManager:
class ProjectManager(BaseService):
def __init__(self, project_dir: str, project_name: str, app_names: list, metadata: dict):
super().__init__(project_root=os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir))
self.project_dir = project_dir
self.project_name = project_name
self.app_names = app_names
self.metadata = metadata
self.project_root = os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir)
# self.project_root is set by BaseService via super().__init__ but we need to ensure it matches logic
# logic was: self.project_root = os.getcwd() if project_dir == "." else os.path.join(os.getcwd(), project_dir)
# which I passed to super. So I can remove the line setting self.project_root here.
self.module_name = metadata.get("project_module_name") or self.project_name
def create_project(self) -> None:
@@ -44,10 +41,12 @@ class ProjectManager:
apps_base_dir = self._get_apps_base_dir()
if apps_base_dir != self.project_root:
create_directory_with_init(apps_base_dir, f"Created {os.path.basename(apps_base_dir)}/__init__.py")
CommonUtils.create_directory_with_init(
apps_base_dir, f"Created {os.path.basename(apps_base_dir)}/__init__.py"
)
# Calculate module paths for correct AppConfig generation (e.g. src.users)
app_module_paths = calculate_app_module_paths(self.app_names, self.metadata)
app_module_paths = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
for i, app_name in enumerate(self.app_names):
app_module = app_module_paths[i]
@@ -58,30 +57,30 @@ class ProjectManager:
def add_apps_to_settings(self) -> None:
"""Add all apps to USER_DEFINED_APPS in base.py settings file."""
base_settings_path = get_base_settings_path(self.project_root, self.module_name)
base_settings_path = CommonUtils.get_base_settings_path(self.project_root, self.module_name)
if not os.path.exists(base_settings_path):
from djinit.utils.exceptions import ConfigError
raise ConfigError("Could not find base.py settings file")
content = read_base_settings(self.project_root, self.module_name)
content = CommonUtils.read_base_settings(self.project_root, self.module_name)
if content is None:
from djinit.utils.exceptions import ConfigError
raise ConfigError("Could not read base.py settings file")
app_module_paths = calculate_app_module_paths(self.app_names, self.metadata)
app_module_paths = CommonUtils.calculate_app_module_paths(self.app_names, self.metadata)
# Transform to full AppConfig paths (e.g. apps.users.apps.UsersConfig)
full_config_paths = []
for app in app_module_paths:
short_name = app.split('.')[-1]
config_name = short_name.title().replace('_', '') + 'Config'
short_name = app.split(".")[-1]
config_name = short_name.title().replace("_", "") + "Config"
full_config_paths.append(f"{app}.apps.{config_name}")
existing_apps = extract_existing_apps(content)
existing_apps = CommonUtils.extract_existing_apps(content)
# Check against both module path and full config path to avoid duplicates
apps_to_add = []
for i, full_path in enumerate(full_config_paths):
@@ -89,11 +88,11 @@ class ProjectManager:
if full_path not in existing_apps and mod_path not in existing_apps:
apps_to_add.append(full_path)
elif mod_path in existing_apps:
# If legacy module path exists, we could upgrade it here,
# If legacy module path exists, we could upgrade it here,
# but ProjectManager is typically initialized on creation.
# For now, let's just not add a duplicate.
# Actually, let's upgrade it if we can
pass
# Actually, let's upgrade it if we can
pass
# If we found apps to add, insert them
if not apps_to_add:
@@ -102,20 +101,19 @@ class ProjectManager:
for i, full_path in enumerate(full_config_paths):
mod_path = app_module_paths[i]
if mod_path in existing_apps and full_path not in existing_apps:
from djinit.utils.common import replace_app_in_user_defined_apps
content = replace_app_in_user_defined_apps(content, mod_path, full_path)
upgraded = True
content = CommonUtils.replace_app_in_user_defined_apps(content, mod_path, full_path)
upgraded = True
if upgraded:
with open(base_settings_path, "w") as f:
with open(base_settings_path, "w") as f:
f.write(content)
UIFormatter.print_success("Upgraded existing apps to full AppConfig paths")
return
UIFormatter.print_success("Upgraded existing apps to full AppConfig paths")
return
UIFormatter.print_success("All apps already configured in USER_DEFINED_APPS")
return
updated_content = insert_apps_into_user_defined_apps(content, apps_to_add)
updated_content = CommonUtils.insert_apps_into_user_defined_apps(content, apps_to_add)
if not updated_content:
from djinit.utils.exceptions import ConfigError
+5 -2
View File
@@ -7,10 +7,11 @@ import os
from pathlib import Path
from typing import Any, Dict
from djinit.core.base import BaseService
from djinit.core.parser import InFileLogicParser
class TemplateEngine:
class TemplateEngine(BaseService):
"""Template engine for rendering file templates."""
def __init__(self, template_dir: str = None):
@@ -23,6 +24,8 @@ class TemplateEngine:
# djinit/services/templates.py -> djinit/services -> djinit -> templates
template_dir = Path(__file__).resolve().parent.parent / "templates"
super().__init__()
if not template_dir.exists():
raise FileNotFoundError(f"Template directory not found: {template_dir}")
@@ -43,7 +46,7 @@ class TemplateEngine:
if not os.path.exists(template_path):
raise FileNotFoundError(f"Template not found: {template_path}")
with open(template_path, "r") as f:
with open(template_path) as f:
template_text = f.read()
return self.parser.render(template_text, context)