|
14 | 14 | from .condition import make_condition
|
15 | 15 | from .expression import QueryExpression
|
16 | 16 | from . import blob
|
17 |
| -from .utils import user_choice |
| 17 | +from .utils import user_choice, get_master |
18 | 18 | from .heading import Heading
|
19 | 19 | from .errors import (DuplicateError, AccessError, DataJointError, UnknownAttributeError,
|
20 | 20 | IntegrityError)
|
@@ -383,15 +383,13 @@ def _delete_cascade(self):
|
383 | 383 | )).fetchall())))
|
384 | 384 | match['parent'] = match['parent'][0]
|
385 | 385 |
|
386 |
| - # If child is a part table of another table, do not recurse (See issue #151) |
387 |
| - if '__' in match['child'] and (not match['child'].strip('`').startswith( |
388 |
| - self.full_table_name).strip('`') + '__'): |
| 386 | + # Avoid deleting from child before master (See issue #151) |
| 387 | + master = get_master(match['child']) |
| 388 | + if master and self.full_table_name != master: |
389 | 389 | raise DataJointError(
|
390 | 390 | 'Attempt to delete from part table {part} before deleting from '
|
391 | 391 | 'its master. Delete from {master} first.'.format(
|
392 |
| - part=match['child'], |
393 |
| - master=match['child'].split('__')[0] + '`' |
394 |
| - )) |
| 392 | + part=match['child'], master=master)) |
395 | 393 |
|
396 | 394 | # Restrict child by self if
|
397 | 395 | # 1. if self's restriction attributes are not in child's primary key
|
@@ -490,8 +488,17 @@ def drop(self):
|
490 | 488 | ' Call drop() on the unrestricted Table.')
|
491 | 489 | self.connection.dependencies.load()
|
492 | 490 | do_drop = True
|
493 |
| - tables = [table for table in self.connection.dependencies.descendants(self.full_table_name) |
494 |
| - if not table.isdigit()] |
| 491 | + tables = [table for table in self.connection.dependencies.descendants( |
| 492 | + self.full_table_name) if not table.isdigit()] |
| 493 | + |
| 494 | + # avoid dropping part tables without their masters: See issue #374 |
| 495 | + for part in tables: |
| 496 | + master = get_master(part) |
| 497 | + if master and master not in tables: |
| 498 | + raise DataJointError( |
| 499 | + 'Attempt to drop part table {part} before dropping ' |
| 500 | + 'its master. Drop {master} first.'.format(part=part, master=master)) |
| 501 | + |
495 | 502 | if config['safemode']:
|
496 | 503 | for table in tables:
|
497 | 504 | print(table, '(%d tuples)' % len(FreeTable(self.connection, table)))
|
@@ -692,7 +699,7 @@ def check_fields(fields):
|
692 | 699 | if field not in self.heading:
|
693 | 700 | raise KeyError(u'`{0:s}` is not in the table heading'.format(field))
|
694 | 701 | elif set(field_list) != set(fields).intersection(self.heading.names):
|
695 |
| - raise DataJointError('Attempt to insert rows with different fields') |
| 702 | + raise DataJointError('Attempt to insert rows with different fields.') |
696 | 703 |
|
697 | 704 | if isinstance(row, np.void): # np.array
|
698 | 705 | check_fields(row.dtype.fields)
|
|
0 commit comments