Allow creation of foreign key on non primary key

This commit is contained in:
Cédric Krier committed 2024-02-22 19:08:43 +01:00
1 parent 2f84af4adf
commit e0f11efc94
6 files changed
+44 -19

No files matched your search

+1
View File
@@ -1,3 +1,4 @@
* Allow creation of foreign key on non primary key
* Include context information in access error
* Remove trytond-worker max tasks per child option for Python <= 3.10
* Do not copy readonly relational fields
+6 -5
View File
@@ -279,15 +279,16 @@ TableHandler
The ``default`` is a method that return the value to fill the new column.
``comment`` set as comment for the column.
.. method:: TableHandler.add_fk(column_name, reference[, on_delete])
.. method:: TableHandler.add_fk(columns, reference[, ref_columns[, on_delete])
Add a foreign key constraint on the named column to target the ``reference``
table name.
Add a foreign key constraint on the named columns to target the
``ref_columns`` of ``reference`` table name.
``on_delete`` defines the method to use when foreign record is deleted.
.. method:: TableHandler.drop_fk(column_name[, table])
.. method:: TableHandler.drop_fk(columns[, ref_columns[, table])
Drop the foreign key constrant on the named column.
Drop the foreign key constraint on the named columns targeting the
``ref_columns``.
``table`` can be used to alter another table.
.. method:: TableHandler.not_null_action(column_name[, action])
+32 -9
View File
@@ -396,40 +396,63 @@ class TableHandler(TableHandlerInterface):
self._update_definitions(columns=True)
def add_fk(self, column_name, reference, on_delete=None):
def add_fk(self, columns, reference, ref_columns=None, on_delete=None):
if on_delete is not None:
on_delete = on_delete.upper()
else:
on_delete = 'SET NULL'
if isinstance(columns, str):
columns = [columns]
cursor = Transaction().connection.cursor()
name = self.convert_name(self.table_name + '_' + column_name + '_fkey')
if ref_columns:
ref_columns_name = '_' + '_'.join(ref_columns)
else:
ref_columns_name = ''
name = self.convert_name(
self.table_name + '_' + '_'.join(columns)
+ ref_columns_name + '_fkey')
if name in self._constraints:
if self._fk_deltypes.get(column_name) != on_delete:
self.drop_fk(column_name)
add = True
for column_name in columns:
if self._fk_deltypes.get(column_name) != on_delete:
self.drop_fk(columns, ref_columns)
add = True
break
else:
add = False
else:
add = True
if add:
columns = SQL(', ').join(map(Identifier, columns))
if not ref_columns:
ref_columns = ['id']
ref_columns = SQL(', ').join(map(Identifier, ref_columns))
cursor.execute(
SQL(
"ALTER TABLE {table} "
"ADD CONSTRAINT {constraint} "
"FOREIGN KEY ({column}) REFERENCES {reference} "
"FOREIGN KEY ({columns}) "
"REFERENCES {reference} ({ref_columns}) "
"ON DELETE {action}"
)
.format(
table=Identifier(self.table_name),
constraint=Identifier(name),
column=Identifier(column_name),
columns=columns,
reference=Identifier(reference),
ref_columns=ref_columns,
action=SQL(on_delete)))
self._update_definitions(constraints=True)
def drop_fk(self, column_name, table=None):
self.drop_constraint(column_name + '_fkey', table=table)
def drop_fk(self, columns, ref_columns=None, table=None):
if isinstance(columns, str):
columns = [columns]
if ref_columns:
ref_columns_name = '_' + '_'.join(ref_columns)
else:
ref_columns_name = ''
self.drop_constraint(
'_'.join(columns) + ref_columns_name + '_fkey', table=table)
def not_null_action(self, column_name, action='add'):
if not self.column_exist(column_name):
+2 -2
View File
@@ -255,10 +255,10 @@ class TableHandler(TableHandlerInterface):
self._update_definitions(columns=True)
def add_fk(self, column_name, reference, on_delete=None):
def add_fk(self, columns, reference, ref_columns=None, on_delete=None):
warnings.warn('Unable to add foreign key with SQLite backend')
def drop_fk(self, column_name, table=None):
def drop_fk(self, columns=None, ref_columns=None, table=None):
warnings.warn('Unable to drop foreign key with SQLite backend')
def not_null_action(self, column_name, action='add'):
+2 -2
View File
@@ -70,10 +70,10 @@ class TableHandlerInterface(object):
def add_column(self, column_name, abstract_type, default=None, comment=''):
raise NotImplementedError
def add_fk(self, column_name, reference, on_delete=None):
def add_fk(self, columns, reference, ref_columns=None, on_delete=None):
raise NotImplementedError
def drop_fk(self, column_name, table=None):
def drop_fk(self, columns, ref_columns=None, table=None):
raise NotImplementedError
def not_null_action(self, column_name, action='add'):
+1 -1
View File
@@ -454,7 +454,7 @@ class ModelSQL(ModelStorage):
# migration from 3.6
table.drop_fk(field_name)
elif ref:
table.add_fk(field_name, ref, field.ondelete)
table.add_fk(field_name, ref, on_delete=field.ondelete)
required = field.required
# Do not set 'NOT NULL' for Binary field as the database column