naming.py 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. # sqlalchemy/naming.py
  2. # Copyright (C) 2005-2017 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: http://www.opensource.org/licenses/mit-license.php
  7. """Establish constraint and index naming conventions.
  8. """
  9. from .schema import Constraint, ForeignKeyConstraint, PrimaryKeyConstraint, \
  10. UniqueConstraint, CheckConstraint, Index, Table, Column
  11. from .. import event, events
  12. from .. import exc
  13. from .elements import _truncated_label, _defer_name, _defer_none_name, conv
  14. import re
  15. class ConventionDict(object):
  16. def __init__(self, const, table, convention):
  17. self.const = const
  18. self._is_fk = isinstance(const, ForeignKeyConstraint)
  19. self.table = table
  20. self.convention = convention
  21. self._const_name = const.name
  22. def _key_table_name(self):
  23. return self.table.name
  24. def _column_X(self, idx):
  25. if self._is_fk:
  26. fk = self.const.elements[idx]
  27. return fk.parent
  28. else:
  29. return list(self.const.columns)[idx]
  30. def _key_constraint_name(self):
  31. if isinstance(self._const_name, (type(None), _defer_none_name)):
  32. raise exc.InvalidRequestError(
  33. "Naming convention including "
  34. "%(constraint_name)s token requires that "
  35. "constraint is explicitly named."
  36. )
  37. if not isinstance(self._const_name, conv):
  38. self.const.name = None
  39. return self._const_name
  40. def _key_column_X_name(self, idx):
  41. return self._column_X(idx).name
  42. def _key_column_X_label(self, idx):
  43. return self._column_X(idx)._label
  44. def _key_referred_table_name(self):
  45. fk = self.const.elements[0]
  46. refs = fk.target_fullname.split(".")
  47. if len(refs) == 3:
  48. refschema, reftable, refcol = refs
  49. else:
  50. reftable, refcol = refs
  51. return reftable
  52. def _key_referred_column_X_name(self, idx):
  53. fk = self.const.elements[idx]
  54. refs = fk.target_fullname.split(".")
  55. if len(refs) == 3:
  56. refschema, reftable, refcol = refs
  57. else:
  58. reftable, refcol = refs
  59. return refcol
  60. def __getitem__(self, key):
  61. if key in self.convention:
  62. return self.convention[key](self.const, self.table)
  63. elif hasattr(self, '_key_%s' % key):
  64. return getattr(self, '_key_%s' % key)()
  65. else:
  66. col_template = re.match(r".*_?column_(\d+)_.+", key)
  67. if col_template:
  68. idx = col_template.group(1)
  69. attr = "_key_" + key.replace(idx, "X")
  70. idx = int(idx)
  71. if hasattr(self, attr):
  72. return getattr(self, attr)(idx)
  73. raise KeyError(key)
  74. _prefix_dict = {
  75. Index: "ix",
  76. PrimaryKeyConstraint: "pk",
  77. CheckConstraint: "ck",
  78. UniqueConstraint: "uq",
  79. ForeignKeyConstraint: "fk"
  80. }
  81. def _get_convention(dict_, key):
  82. for super_ in key.__mro__:
  83. if super_ in _prefix_dict and _prefix_dict[super_] in dict_:
  84. return dict_[_prefix_dict[super_]]
  85. elif super_ in dict_:
  86. return dict_[super_]
  87. else:
  88. return None
  89. def _constraint_name_for_table(const, table):
  90. metadata = table.metadata
  91. convention = _get_convention(metadata.naming_convention, type(const))
  92. if isinstance(const.name, conv):
  93. return const.name
  94. elif convention is not None and \
  95. not isinstance(const.name, conv) and \
  96. (
  97. const.name is None or
  98. "constraint_name" in convention or
  99. isinstance(const.name, _defer_name)):
  100. return conv(
  101. convention % ConventionDict(const, table,
  102. metadata.naming_convention)
  103. )
  104. elif isinstance(convention, _defer_none_name):
  105. return None
  106. @event.listens_for(Constraint, "after_parent_attach")
  107. @event.listens_for(Index, "after_parent_attach")
  108. def _constraint_name(const, table):
  109. if isinstance(table, Column):
  110. # for column-attached constraint, set another event
  111. # to link the column attached to the table as this constraint
  112. # associated with the table.
  113. event.listen(table, "after_parent_attach",
  114. lambda col, table: _constraint_name(const, table)
  115. )
  116. elif isinstance(table, Table):
  117. if isinstance(const.name, (conv, _defer_name)):
  118. return
  119. newname = _constraint_name_for_table(const, table)
  120. if newname is not None:
  121. const.name = newname