sera-2 1.16.0__tar.gz → 1.17.0__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.
Files changed (41) hide show
  1. {sera_2-1.16.0 → sera_2-1.17.0}/PKG-INFO +1 -1
  2. {sera_2-1.16.0 → sera_2-1.17.0}/pyproject.toml +1 -1
  3. {sera_2-1.16.0 → sera_2-1.17.0}/sera/exports/schema.py +14 -0
  4. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/make_python_model.py +38 -4
  5. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_parse.py +1 -0
  6. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_property.py +6 -0
  7. {sera_2-1.16.0 → sera_2-1.17.0}/sera/typing.py +1 -0
  8. {sera_2-1.16.0 → sera_2-1.17.0}/README.md +0 -0
  9. {sera_2-1.16.0 → sera_2-1.17.0}/sera/__init__.py +0 -0
  10. {sera_2-1.16.0 → sera_2-1.17.0}/sera/constants.py +0 -0
  11. {sera_2-1.16.0 → sera_2-1.17.0}/sera/exports/__init__.py +0 -0
  12. {sera_2-1.16.0 → sera_2-1.17.0}/sera/exports/test.py +0 -0
  13. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/__init__.py +0 -0
  14. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/api_helper.py +0 -0
  15. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/api_test_helper.py +0 -0
  16. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/base_orm.py +0 -0
  17. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/base_service.py +0 -0
  18. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/dag/__init__.py +0 -0
  19. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/dag/_dag.py +0 -0
  20. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/middlewares/__init__.py +0 -0
  21. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/middlewares/auth.py +0 -0
  22. {sera_2-1.16.0 → sera_2-1.17.0}/sera/libs/middlewares/uscp.py +0 -0
  23. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/__init__.py +0 -0
  24. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/__main__.py +0 -0
  25. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/make_app.py +0 -0
  26. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/make_python_api.py +0 -0
  27. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/make_python_services.py +0 -0
  28. {sera_2-1.16.0 → sera_2-1.17.0}/sera/make/make_typescript_model.py +0 -0
  29. {sera_2-1.16.0 → sera_2-1.17.0}/sera/misc/__init__.py +0 -0
  30. {sera_2-1.16.0 → sera_2-1.17.0}/sera/misc/_formatter.py +0 -0
  31. {sera_2-1.16.0 → sera_2-1.17.0}/sera/misc/_utils.py +0 -0
  32. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/__init__.py +0 -0
  33. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_class.py +0 -0
  34. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_collection.py +0 -0
  35. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_constraints.py +0 -0
  36. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_datatype.py +0 -0
  37. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_default.py +0 -0
  38. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_enum.py +0 -0
  39. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_module.py +0 -0
  40. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_multi_lingual_string.py +0 -0
  41. {sera_2-1.16.0 → sera_2-1.17.0}/sera/models/_schema.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sera-2
3
- Version: 1.16.0
3
+ Version: 1.17.0
4
4
  Summary:
5
5
  Author: Binh Vu
6
6
  Author-email: bvu687@gmail.com
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "sera-2"
3
- version = "1.16.0"
3
+ version = "1.17.0"
4
4
  description = ""
5
5
  authors = ["Binh Vu <bvu687@gmail.com>"]
6
6
  readme = "README.md"
@@ -54,6 +54,20 @@ def export_tbls(schema: Schema, outfile: Path):
54
54
  "columns": [prop.name],
55
55
  }
56
56
  )
57
+
58
+ if prop.db.foreign_key is not None:
59
+ idprop = assert_not_null(prop.db.foreign_key.get_id_property())
60
+ out["relations"].append(
61
+ {
62
+ "table": cls.name,
63
+ "columns": [prop.name],
64
+ "cardinality": "zero_or_one",
65
+ "parent_table": prop.db.foreign_key.name,
66
+ "parent_columns": [idprop_name],
67
+ "parent_cardinality": "zero_or_one",
68
+ "def": f"FOREIGN KEY ({prop.name}) REFERENCES {prop.db.foreign_key.name}({idprop_name})",
69
+ }
70
+ )
57
71
  else:
58
72
  if prop.cardinality == Cardinality.MANY_TO_MANY:
59
73
  # For many-to-many relationships, we need to create a join table
@@ -1079,12 +1079,10 @@ def make_python_relational_model(
1079
1079
 
1080
1080
  target_pkg.module("base").write(program)
1081
1081
 
1082
- custom_types: list[ObjectProperty] = []
1083
-
1084
- for cls in schema.topological_sort():
1082
+ def make_orm(cls: Class):
1085
1083
  if cls.db is None or cls.name in reference_classes:
1086
1084
  # skip classes that are not stored in the database
1087
- continue
1085
+ return
1088
1086
 
1089
1087
  program = Program()
1090
1088
  program.import_("__future__.annotations", True)
@@ -1093,6 +1091,11 @@ def make_python_relational_model(
1093
1091
  program.import_("sqlalchemy.orm.Mapped", True)
1094
1092
  program.import_(f"{target_pkg.path}.base.Base", True)
1095
1093
 
1094
+ ident_manager = ImportHelper(
1095
+ program,
1096
+ GLOBAL_IDENTS,
1097
+ )
1098
+
1096
1099
  index_stmts = []
1097
1100
  if len(cls.db.indices) > 0:
1098
1101
  program.import_("sqlalchemy.Index", True)
@@ -1159,6 +1162,32 @@ def make_python_relational_model(
1159
1162
  proptype = f"Mapped[{sqltype.mapped_pytype}]"
1160
1163
 
1161
1164
  propvalargs: list[expr.Expr] = [expr.ExprIdent(sqltype.type)]
1165
+ if prop.db.foreign_key is not None:
1166
+ assert (
1167
+ prop.db.foreign_key.db is not None
1168
+ ), f"Foreign key {prop.db.foreign_key.name} must have a database mapping"
1169
+ foreign_key_idprop = prop.db.foreign_key.get_id_property()
1170
+ assert (
1171
+ foreign_key_idprop is not None
1172
+ ), f"Foreign key {prop.db.foreign_key.name} must have an id property"
1173
+ propvalargs.append(
1174
+ expr.ExprFuncCall(
1175
+ ident_manager.use("ForeignKey"),
1176
+ [
1177
+ expr.ExprConstant(
1178
+ f"{prop.db.foreign_key.db.table_name}.{foreign_key_idprop.name}"
1179
+ ),
1180
+ PredefinedFn.keyword_assignment(
1181
+ "ondelete",
1182
+ expr.ExprConstant("CASCADE"),
1183
+ ),
1184
+ PredefinedFn.keyword_assignment(
1185
+ "onupdate",
1186
+ expr.ExprConstant("CASCADE"),
1187
+ ),
1188
+ ],
1189
+ )
1190
+ )
1162
1191
  if prop.db.is_primary_key:
1163
1192
  propvalargs.append(
1164
1193
  PredefinedFn.keyword_assignment(
@@ -1208,6 +1237,11 @@ def make_python_relational_model(
1208
1237
 
1209
1238
  target_pkg.module(cls.get_pymodule_name()).write(program)
1210
1239
 
1240
+ custom_types: list[ObjectProperty] = []
1241
+
1242
+ for cls in schema.topological_sort():
1243
+ make_orm(cls)
1244
+
1211
1245
  # make a base class that implements the mapping for custom types
1212
1246
  custom_types = filter_duplication(
1213
1247
  custom_types, lambda p: (p.target.name, p.cardinality, p.is_optional, p.is_map)
@@ -160,6 +160,7 @@ def _parse_property(
160
160
  is_indexed=db.get("is_indexed", False)
161
161
  or db.get("is_unique", False)
162
162
  or db.get("is_primary_key", False),
163
+ foreign_key=schema.classes.get(db.get("foreign_key")),
163
164
  )
164
165
  if "db" in prop
165
166
  else None
@@ -150,6 +150,12 @@ class DataPropDBInfo:
150
150
  is_unique: bool = False
151
151
  # whether this property is indexed or not
152
152
  is_indexed: bool = False
153
+ # this is used in conjunction with is_primary_key = True for the case of
154
+ # extending a table with frequently updated properties. The value for the `foreign_key`
155
+ # will be a target class. The cardinality is one-to-one, on target class deletion,
156
+ # this class will be deleted as well (because it's an extended table of the target class).
157
+ # on source (this class) deletion, the target class will not be deleted.
158
+ foreign_key: Optional[Class] = None
153
159
 
154
160
 
155
161
  @dataclass(kw_only=True)
@@ -37,4 +37,5 @@ GLOBAL_IDENTS = {
37
37
  "AsyncSession": "sqlalchemy.ext.asyncio.AsyncSession",
38
38
  "ASGIConnection": "litestar.connection.ASGIConnection",
39
39
  "UNSET": "sera.typing.UNSET",
40
+ "ForeignKey": "sqlalchemy.ForeignKey",
40
41
  }
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