Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/sqlalchemy/sql/naming.py : 70%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# sqlalchemy/naming.py
2# Copyright (C) 2005-2020 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
8"""Establish constraint and index naming conventions.
11"""
13import re
15from .elements import _defer_name
16from .elements import _defer_none_name
17from .elements import conv
18from .schema import CheckConstraint
19from .schema import Column
20from .schema import Constraint
21from .schema import ForeignKeyConstraint
22from .schema import Index
23from .schema import PrimaryKeyConstraint
24from .schema import Table
25from .schema import UniqueConstraint
26from .. import event
27from .. import events # noqa
28from .. import exc
31class ConventionDict(object):
32 def __init__(self, const, table, convention):
33 self.const = const
34 self._is_fk = isinstance(const, ForeignKeyConstraint)
35 self.table = table
36 self.convention = convention
37 self._const_name = const.name
39 def _key_table_name(self):
40 return self.table.name
42 def _column_X(self, idx):
43 if self._is_fk:
44 fk = self.const.elements[idx]
45 return fk.parent
46 else:
47 return list(self.const.columns)[idx]
49 def _key_constraint_name(self):
50 if isinstance(self._const_name, (type(None), _defer_none_name)):
51 raise exc.InvalidRequestError(
52 "Naming convention including "
53 "%(constraint_name)s token requires that "
54 "constraint is explicitly named."
55 )
56 if not isinstance(self._const_name, conv):
57 self.const.name = None
58 return self._const_name
60 def _key_column_X_key(self, idx):
61 # note this method was missing before
62 # [ticket:3989], meaning tokens like ``%(column_0_key)s`` weren't
63 # working even though documented.
64 return self._column_X(idx).key
66 def _key_column_X_name(self, idx):
67 return self._column_X(idx).name
69 def _key_column_X_label(self, idx):
70 return self._column_X(idx)._ddl_label
72 def _key_referred_table_name(self):
73 fk = self.const.elements[0]
74 refs = fk.target_fullname.split(".")
75 if len(refs) == 3:
76 refschema, reftable, refcol = refs
77 else:
78 reftable, refcol = refs
79 return reftable
81 def _key_referred_column_X_name(self, idx):
82 fk = self.const.elements[idx]
83 # note that before [ticket:3989], this method was returning
84 # the specification for the :class:`.ForeignKey` itself, which normally
85 # would be using the ``.key`` of the column, not the name.
86 return fk.column.name
88 def __getitem__(self, key):
89 if key in self.convention:
90 return self.convention[key](self.const, self.table)
91 elif hasattr(self, "_key_%s" % key):
92 return getattr(self, "_key_%s" % key)()
93 else:
94 col_template = re.match(r".*_?column_(\d+)(_?N)?_.+", key)
95 if col_template:
96 idx = col_template.group(1)
97 multiples = col_template.group(2)
99 if multiples:
100 if self._is_fk:
101 elems = self.const.elements
102 else:
103 elems = list(self.const.columns)
104 tokens = []
105 for idx, elem in enumerate(elems):
106 attr = "_key_" + key.replace("0" + multiples, "X")
107 try:
108 tokens.append(getattr(self, attr)(idx))
109 except AttributeError:
110 raise KeyError(key)
111 sep = "_" if multiples.startswith("_") else ""
112 return sep.join(tokens)
113 else:
114 attr = "_key_" + key.replace(idx, "X")
115 idx = int(idx)
116 if hasattr(self, attr):
117 return getattr(self, attr)(idx)
118 raise KeyError(key)
121_prefix_dict = {
122 Index: "ix",
123 PrimaryKeyConstraint: "pk",
124 CheckConstraint: "ck",
125 UniqueConstraint: "uq",
126 ForeignKeyConstraint: "fk",
127}
130def _get_convention(dict_, key):
132 for super_ in key.__mro__:
133 if super_ in _prefix_dict and _prefix_dict[super_] in dict_:
134 return dict_[_prefix_dict[super_]]
135 elif super_ in dict_:
136 return dict_[super_]
137 else:
138 return None
141def _constraint_name_for_table(const, table):
142 metadata = table.metadata
143 convention = _get_convention(metadata.naming_convention, type(const))
145 if isinstance(const.name, conv):
146 return const.name
147 elif (
148 convention is not None
149 and not isinstance(const.name, conv)
150 and (
151 const.name is None
152 or "constraint_name" in convention
153 or isinstance(const.name, _defer_name)
154 )
155 ):
156 return conv(
157 convention
158 % ConventionDict(const, table, metadata.naming_convention)
159 )
160 elif isinstance(convention, _defer_none_name):
161 return None
164@event.listens_for(Constraint, "after_parent_attach")
165@event.listens_for(Index, "after_parent_attach")
166def _constraint_name(const, table):
167 if isinstance(table, Column):
168 # for column-attached constraint, set another event
169 # to link the column attached to the table as this constraint
170 # associated with the table.
171 event.listen(
172 table,
173 "after_parent_attach",
174 lambda col, table: _constraint_name(const, table),
175 )
176 elif isinstance(table, Table):
177 if isinstance(const.name, (conv, _defer_name)):
178 return
180 newname = _constraint_name_for_table(const, table)
181 if newname is not None:
182 const.name = newname