core-framework 0.12.13__tar.gz → 0.12.15__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {core_framework-0.12.13 → core_framework-0.12.15}/PKG-INFO +1 -1
- {core_framework-0.12.13 → core_framework-0.12.15}/core/__init__.py +1 -1
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/engine.py +44 -1
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/operations.py +97 -18
- {core_framework-0.12.13 → core_framework-0.12.15}/pyproject.toml +1 -1
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_issues_fixes.py +25 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/.gitignore +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/README.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/app.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/backends.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/base.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/decorators.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/hashers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/helpers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/middleware.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/models.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/permissions.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/schemas.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/tokens.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/auth/views.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/choices.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/cli/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/cli/main.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/config.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/database.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/datetime.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/dependencies.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/docker.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/kubernetes.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/deployment/pm2.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/exceptions.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/fields.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/avro.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/base.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/config.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/consumer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/confluent/producer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/decorators.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/admin.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/broker.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/consumer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/kafka/producer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/broker.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/consumer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/rabbitmq/producer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/broker.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/consumer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/redis/producer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/registry.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/topics.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/messaging/workers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/middleware.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/analyzer.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/cli.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/migration.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/migrations/state.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/models.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/permissions.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/querysets.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/relations.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/routing.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/serializers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/base.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/config.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/decorators.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/registry.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/scheduler.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tasks/worker.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/tenancy.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/assertions.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/client.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/database.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/factories.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/mocks.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/testing/plugin.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/validation.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/validators.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/core/views.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/01-quickstart.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/02-viewsets.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/03-authentication.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/04-messaging.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/05-multi-service.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/06-tasks.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/07-deployment.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/08-complete-example.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/09-settings.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/10-migrations.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/11-permissions.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/12-auth-backends.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/13-validators.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/14-querysets.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/15-routing.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/16-serializers.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/17-datetime.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/18-dependencies.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/19-views.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/20-fields.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/21-tenancy.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/22-replicas.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/23-soft-delete.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/24-relations.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/25-exceptions.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/26-choices.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/27-workers.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/28-avro.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/29-topics.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/30-changelog-0.12.2.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/31-middleware.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/32-migration-guide-0.12.2.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/32-testing.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/33-changelog-0.12.3.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/99-faq-troubleshooting.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/GUIDE.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/docs/README.md +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/app.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/auth.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/models.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/schemas.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/example/views.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/libs/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/main.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/__init__.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/conftest.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_auth_helpers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_imports.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_models.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_permissions.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_querysets.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_serializers.py +0 -0
- {core_framework-0.12.13 → core_framework-0.12.15}/tests/test_validation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: core-framework
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.15
|
|
4
4
|
Summary: Core Framework - Django-inspired, FastAPI-powered. Alta performance, baixo acoplamento, produtividade extrema.
|
|
5
5
|
Project-URL: Homepage, https://github.com/SorPuti/core-framework
|
|
6
6
|
Project-URL: Documentation, https://github.com/SorPuti/core-framework#readme
|
|
@@ -52,6 +52,41 @@ if TYPE_CHECKING:
|
|
|
52
52
|
MIGRATIONS_TABLE = "_core_migrations"
|
|
53
53
|
|
|
54
54
|
|
|
55
|
+
def _detect_extra_imports(ops_code: str) -> list[str]:
|
|
56
|
+
"""
|
|
57
|
+
Detect and return extra import statements needed for migration code.
|
|
58
|
+
|
|
59
|
+
Analyzes the generated operation code to find references like:
|
|
60
|
+
- timezone.now -> from core.datetime import timezone
|
|
61
|
+
- AdvancedField.xxx -> from core.fields import AdvancedField
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
ops_code: The generated operation code string
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
List of import statements to add to the migration file
|
|
68
|
+
"""
|
|
69
|
+
imports = set()
|
|
70
|
+
|
|
71
|
+
# Patterns for short-form callable references
|
|
72
|
+
# These match the output of _serialize_default()
|
|
73
|
+
patterns = [
|
|
74
|
+
# timezone.xxx -> from core.datetime import timezone
|
|
75
|
+
# Match timezone.now but not datetime.timezone.now
|
|
76
|
+
(r'(?<![.\w])timezone\.\w+', 'from core.datetime import timezone'),
|
|
77
|
+
# AdvancedField.xxx -> from core.fields import AdvancedField
|
|
78
|
+
(r'(?<![.\w])AdvancedField\.\w+', 'from core.fields import AdvancedField'),
|
|
79
|
+
# datetime.xxx -> from datetime import datetime (for full module path)
|
|
80
|
+
(r'(?<![.\w])datetime\.\w+', 'from datetime import datetime'),
|
|
81
|
+
]
|
|
82
|
+
|
|
83
|
+
for pattern, import_stmt in patterns:
|
|
84
|
+
if re.search(pattern, ops_code):
|
|
85
|
+
imports.add(import_stmt)
|
|
86
|
+
|
|
87
|
+
return sorted(imports)
|
|
88
|
+
|
|
89
|
+
|
|
55
90
|
def _get_migrations_table_sql(dialect: str) -> str:
|
|
56
91
|
"""
|
|
57
92
|
Returns dialect-specific SQL for creating migrations table.
|
|
@@ -442,6 +477,10 @@ class MigrationEngine:
|
|
|
442
477
|
|
|
443
478
|
ops_str = ",\n".join(ops_code) if ops_code else " # No operations"
|
|
444
479
|
|
|
480
|
+
# Detect and add extra imports needed for callable defaults
|
|
481
|
+
extra_imports = _detect_extra_imports(ops_str)
|
|
482
|
+
extra_imports_str = "\n".join(extra_imports) + "\n" if extra_imports else ""
|
|
483
|
+
|
|
445
484
|
return f'''"""
|
|
446
485
|
Migration: {name}
|
|
447
486
|
Generated at: {timezone.now().isoformat()}
|
|
@@ -467,7 +506,7 @@ from core.migrations.operations import (
|
|
|
467
506
|
DropEnum,
|
|
468
507
|
AlterEnum,
|
|
469
508
|
)
|
|
470
|
-
|
|
509
|
+
{extra_imports_str}
|
|
471
510
|
|
|
472
511
|
migration = Migration(
|
|
473
512
|
name="{name}",
|
|
@@ -522,6 +561,10 @@ migration = Migration(
|
|
|
522
561
|
return None
|
|
523
562
|
|
|
524
563
|
operations = self._diff_to_operations(diff)
|
|
564
|
+
|
|
565
|
+
if not operations:
|
|
566
|
+
print("No changes detected.")
|
|
567
|
+
return None
|
|
525
568
|
|
|
526
569
|
# Gera nome da migração
|
|
527
570
|
number = self._get_next_migration_number()
|
|
@@ -26,7 +26,7 @@ def _serialize_default(value: Any) -> str:
|
|
|
26
26
|
- None -> "None"
|
|
27
27
|
- Strings -> repr()
|
|
28
28
|
- Booleans -> "True"/"False"
|
|
29
|
-
- Callables -> "
|
|
29
|
+
- Callables -> short name (e.g., "timezone.now" not "core.datetime.timezone.now")
|
|
30
30
|
- Other -> repr()
|
|
31
31
|
|
|
32
32
|
Args:
|
|
@@ -41,11 +41,21 @@ def _serialize_default(value: Any) -> str:
|
|
|
41
41
|
if callable(value):
|
|
42
42
|
# Get module and function name for proper serialization
|
|
43
43
|
module = getattr(value, "__module__", "")
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if module and
|
|
47
|
-
#
|
|
48
|
-
|
|
44
|
+
qualname = getattr(value, "__qualname__", "") or getattr(value, "__name__", "")
|
|
45
|
+
|
|
46
|
+
if module and qualname:
|
|
47
|
+
# Use short form for known modules to avoid long paths
|
|
48
|
+
# e.g., "core.datetime.timezone.now" -> "timezone.now"
|
|
49
|
+
# The corresponding import will be added to the migration file
|
|
50
|
+
if module == "core.datetime" and qualname.startswith("timezone."):
|
|
51
|
+
return qualname # Returns "timezone.now"
|
|
52
|
+
if module == "core.fields" and qualname.startswith("AdvancedField."):
|
|
53
|
+
return qualname # Returns "AdvancedField.json_field..."
|
|
54
|
+
if module == "datetime":
|
|
55
|
+
return f"datetime.{qualname}" # Returns "datetime.now"
|
|
56
|
+
|
|
57
|
+
# For other modules, return full path
|
|
58
|
+
return f"{module}.{qualname}"
|
|
49
59
|
|
|
50
60
|
# Fallback for lambdas or unnamed functions
|
|
51
61
|
return "None"
|
|
@@ -128,12 +138,58 @@ class ColumnDef:
|
|
|
128
138
|
|
|
129
139
|
return self.type
|
|
130
140
|
|
|
141
|
+
def _format_default_value(self, value: Any, dialect: str) -> str | None:
|
|
142
|
+
"""
|
|
143
|
+
Formata um valor para uso em cláusula DEFAULT SQL.
|
|
144
|
+
|
|
145
|
+
Args:
|
|
146
|
+
value: Valor a ser formatado
|
|
147
|
+
dialect: Nome do dialeto SQL
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
String SQL formatada com DEFAULT ou None
|
|
151
|
+
"""
|
|
152
|
+
from datetime import datetime, date, time
|
|
153
|
+
|
|
154
|
+
if value is None:
|
|
155
|
+
return None
|
|
156
|
+
|
|
157
|
+
if isinstance(value, bool):
|
|
158
|
+
if dialect == "postgresql":
|
|
159
|
+
return f"DEFAULT {'TRUE' if value else 'FALSE'}"
|
|
160
|
+
return f"DEFAULT {1 if value else 0}"
|
|
161
|
+
|
|
162
|
+
if isinstance(value, datetime):
|
|
163
|
+
# Format datetime as ISO string for SQL
|
|
164
|
+
return f"DEFAULT '{value.isoformat()}'"
|
|
165
|
+
|
|
166
|
+
if isinstance(value, date):
|
|
167
|
+
return f"DEFAULT '{value.isoformat()}'"
|
|
168
|
+
|
|
169
|
+
if isinstance(value, time):
|
|
170
|
+
return f"DEFAULT '{value.isoformat()}'"
|
|
171
|
+
|
|
172
|
+
if isinstance(value, str):
|
|
173
|
+
# Escape single quotes in strings
|
|
174
|
+
escaped = value.replace("'", "''")
|
|
175
|
+
return f"DEFAULT '{escaped}'"
|
|
176
|
+
|
|
177
|
+
if isinstance(value, (int, float)):
|
|
178
|
+
return f"DEFAULT {value}"
|
|
179
|
+
|
|
180
|
+
if isinstance(value, (dict, list)):
|
|
181
|
+
# JSON values - serialize and quote
|
|
182
|
+
import json
|
|
183
|
+
json_str = json.dumps(value).replace("'", "''")
|
|
184
|
+
return f"DEFAULT '{json_str}'"
|
|
185
|
+
|
|
186
|
+
# Fallback: convert to string
|
|
187
|
+
return f"DEFAULT '{str(value)}'"
|
|
188
|
+
|
|
131
189
|
def _get_default_sql(self, dialect: str) -> str | None:
|
|
132
190
|
"""
|
|
133
191
|
Gera SQL para valor default considerando o dialeto.
|
|
134
192
|
|
|
135
|
-
Bug #2: Boolean defaults usando TRUE/FALSE para PostgreSQL
|
|
136
|
-
|
|
137
193
|
Args:
|
|
138
194
|
dialect: Nome do dialeto
|
|
139
195
|
|
|
@@ -143,17 +199,19 @@ class ColumnDef:
|
|
|
143
199
|
if self.default is None:
|
|
144
200
|
return None
|
|
145
201
|
|
|
146
|
-
|
|
147
|
-
|
|
202
|
+
# Callable defaults: execute and use the result
|
|
203
|
+
# This is the most robust approach - no need to detect function names
|
|
204
|
+
if callable(self.default):
|
|
205
|
+
try:
|
|
206
|
+
result = self.default()
|
|
207
|
+
# Format the result as SQL DEFAULT
|
|
208
|
+
return self._format_default_value(result, dialect)
|
|
209
|
+
except Exception:
|
|
210
|
+
# If callable fails (needs arguments, etc.), skip DEFAULT
|
|
211
|
+
return None
|
|
148
212
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
if dialect == "postgresql":
|
|
152
|
-
return f"DEFAULT {'TRUE' if self.default else 'FALSE'}"
|
|
153
|
-
else:
|
|
154
|
-
return f"DEFAULT {1 if self.default else 0}"
|
|
155
|
-
|
|
156
|
-
return f"DEFAULT {self.default}"
|
|
213
|
+
# Non-callable values: format directly
|
|
214
|
+
return self._format_default_value(self.default, dialect)
|
|
157
215
|
|
|
158
216
|
def to_sql(self, dialect: str = "sqlite") -> str:
|
|
159
217
|
"""Gera SQL para a coluna, adaptado ao dialeto do banco."""
|
|
@@ -446,6 +504,27 @@ class AlterColumn(Operation):
|
|
|
446
504
|
if self.set_default:
|
|
447
505
|
changes.append(f"default to {self.new_default}")
|
|
448
506
|
return f"Alter column '{self.column_name}' in '{self.table_name}': {', '.join(changes)}"
|
|
507
|
+
|
|
508
|
+
def to_code(self) -> str:
|
|
509
|
+
"""Gera código Python para AlterColumn com serialização correta de callables."""
|
|
510
|
+
new_type_str = f"'{self.new_type}'" if self.new_type else "None"
|
|
511
|
+
old_type_str = f"'{self.old_type}'" if self.old_type else "None"
|
|
512
|
+
|
|
513
|
+
# Serializa defaults corretamente (callables, None, strings, etc.)
|
|
514
|
+
new_default_str = _serialize_default(self.new_default)
|
|
515
|
+
old_default_str = _serialize_default(self.old_default)
|
|
516
|
+
|
|
517
|
+
return f"""AlterColumn(
|
|
518
|
+
table_name='{self.table_name}',
|
|
519
|
+
column_name='{self.column_name}',
|
|
520
|
+
new_type={new_type_str},
|
|
521
|
+
new_nullable={self.new_nullable},
|
|
522
|
+
new_default={new_default_str},
|
|
523
|
+
set_default={self.set_default},
|
|
524
|
+
old_type={old_type_str},
|
|
525
|
+
old_nullable={self.old_nullable},
|
|
526
|
+
old_default={old_default_str},
|
|
527
|
+
)"""
|
|
449
528
|
|
|
450
529
|
|
|
451
530
|
@dataclass
|
|
@@ -251,6 +251,31 @@ class TestIssue5MigrationSerialization:
|
|
|
251
251
|
assert "0x" not in code
|
|
252
252
|
# Should contain proper reference
|
|
253
253
|
assert "CreateTable" in code
|
|
254
|
+
|
|
255
|
+
def test_alter_column_to_code_with_callable(self):
|
|
256
|
+
"""AlterColumn.to_code() should serialize callable defaults correctly."""
|
|
257
|
+
from core.migrations.operations import AlterColumn
|
|
258
|
+
from core.datetime import timezone
|
|
259
|
+
|
|
260
|
+
op = AlterColumn(
|
|
261
|
+
table_name="users",
|
|
262
|
+
column_name="created_at",
|
|
263
|
+
new_type="DATETIME",
|
|
264
|
+
new_default=timezone.now,
|
|
265
|
+
set_default=True,
|
|
266
|
+
old_type="TIMESTAMP",
|
|
267
|
+
old_default=None,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
code = op.to_code()
|
|
271
|
+
|
|
272
|
+
# Should NOT contain <function ... at 0x...>
|
|
273
|
+
assert "<function" not in code, f"Generated invalid code: {code}"
|
|
274
|
+
assert "0x" not in code, f"Generated memory address: {code}"
|
|
275
|
+
# Should be valid Python
|
|
276
|
+
assert "AlterColumn" in code
|
|
277
|
+
# Should have proper serialization
|
|
278
|
+
assert "core.datetime.timezone.now" in code or "timezone.now" in code
|
|
254
279
|
|
|
255
280
|
|
|
256
281
|
class TestIssue6ResetDbCascade:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|