You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

168 lines
5.7KB

  1. # orm/sync.py
  2. # Copyright (C) 2005-2021 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. """private module containing functions used for copying data
  8. between instances based on join conditions.
  9. """
  10. from . import attributes
  11. from . import exc
  12. from . import util as orm_util
  13. from .. import util
  14. def populate(
  15. source,
  16. source_mapper,
  17. dest,
  18. dest_mapper,
  19. synchronize_pairs,
  20. uowcommit,
  21. flag_cascaded_pks,
  22. ):
  23. source_dict = source.dict
  24. dest_dict = dest.dict
  25. for l, r in synchronize_pairs:
  26. try:
  27. # inline of source_mapper._get_state_attr_by_column
  28. prop = source_mapper._columntoproperty[l]
  29. value = source.manager[prop.key].impl.get(
  30. source, source_dict, attributes.PASSIVE_OFF
  31. )
  32. except exc.UnmappedColumnError as err:
  33. _raise_col_to_prop(False, source_mapper, l, dest_mapper, r, err)
  34. try:
  35. # inline of dest_mapper._set_state_attr_by_column
  36. prop = dest_mapper._columntoproperty[r]
  37. dest.manager[prop.key].impl.set(dest, dest_dict, value, None)
  38. except exc.UnmappedColumnError as err:
  39. _raise_col_to_prop(True, source_mapper, l, dest_mapper, r, err)
  40. # technically the "r.primary_key" check isn't
  41. # needed here, but we check for this condition to limit
  42. # how often this logic is invoked for memory/performance
  43. # reasons, since we only need this info for a primary key
  44. # destination.
  45. if (
  46. flag_cascaded_pks
  47. and l.primary_key
  48. and r.primary_key
  49. and r.references(l)
  50. ):
  51. uowcommit.attributes[("pk_cascaded", dest, r)] = True
  52. def bulk_populate_inherit_keys(source_dict, source_mapper, synchronize_pairs):
  53. # a simplified version of populate() used by bulk insert mode
  54. for l, r in synchronize_pairs:
  55. try:
  56. prop = source_mapper._columntoproperty[l]
  57. value = source_dict[prop.key]
  58. except exc.UnmappedColumnError as err:
  59. _raise_col_to_prop(False, source_mapper, l, source_mapper, r, err)
  60. try:
  61. prop = source_mapper._columntoproperty[r]
  62. source_dict[prop.key] = value
  63. except exc.UnmappedColumnError:
  64. _raise_col_to_prop(True, source_mapper, l, source_mapper, r)
  65. def clear(dest, dest_mapper, synchronize_pairs):
  66. for l, r in synchronize_pairs:
  67. if (
  68. r.primary_key
  69. and dest_mapper._get_state_attr_by_column(dest, dest.dict, r)
  70. not in orm_util._none_set
  71. ):
  72. raise AssertionError(
  73. "Dependency rule tried to blank-out primary key "
  74. "column '%s' on instance '%s'" % (r, orm_util.state_str(dest))
  75. )
  76. try:
  77. dest_mapper._set_state_attr_by_column(dest, dest.dict, r, None)
  78. except exc.UnmappedColumnError as err:
  79. _raise_col_to_prop(True, None, l, dest_mapper, r, err)
  80. def update(source, source_mapper, dest, old_prefix, synchronize_pairs):
  81. for l, r in synchronize_pairs:
  82. try:
  83. oldvalue = source_mapper._get_committed_attr_by_column(
  84. source.obj(), l
  85. )
  86. value = source_mapper._get_state_attr_by_column(
  87. source, source.dict, l, passive=attributes.PASSIVE_OFF
  88. )
  89. except exc.UnmappedColumnError as err:
  90. _raise_col_to_prop(False, source_mapper, l, None, r, err)
  91. dest[r.key] = value
  92. dest[old_prefix + r.key] = oldvalue
  93. def populate_dict(source, source_mapper, dict_, synchronize_pairs):
  94. for l, r in synchronize_pairs:
  95. try:
  96. value = source_mapper._get_state_attr_by_column(
  97. source, source.dict, l, passive=attributes.PASSIVE_OFF
  98. )
  99. except exc.UnmappedColumnError as err:
  100. _raise_col_to_prop(False, source_mapper, l, None, r, err)
  101. dict_[r.key] = value
  102. def source_modified(uowcommit, source, source_mapper, synchronize_pairs):
  103. """return true if the source object has changes from an old to a
  104. new value on the given synchronize pairs
  105. """
  106. for l, r in synchronize_pairs:
  107. try:
  108. prop = source_mapper._columntoproperty[l]
  109. except exc.UnmappedColumnError as err:
  110. _raise_col_to_prop(False, source_mapper, l, None, r, err)
  111. history = uowcommit.get_attribute_history(
  112. source, prop.key, attributes.PASSIVE_NO_INITIALIZE
  113. )
  114. if bool(history.deleted):
  115. return True
  116. else:
  117. return False
  118. def _raise_col_to_prop(
  119. isdest, source_mapper, source_column, dest_mapper, dest_column, err
  120. ):
  121. if isdest:
  122. util.raise_(
  123. exc.UnmappedColumnError(
  124. "Can't execute sync rule for "
  125. "destination column '%s'; mapper '%s' does not map "
  126. "this column. Try using an explicit `foreign_keys` "
  127. "collection which does not include this column (or use "
  128. "a viewonly=True relation)." % (dest_column, dest_mapper)
  129. ),
  130. replace_context=err,
  131. )
  132. else:
  133. util.raise_(
  134. exc.UnmappedColumnError(
  135. "Can't execute sync rule for "
  136. "source column '%s'; mapper '%s' does not map this "
  137. "column. Try using an explicit `foreign_keys` "
  138. "collection which does not include destination column "
  139. "'%s' (or use a viewonly=True relation)."
  140. % (source_column, source_mapper, dest_column)
  141. ),
  142. replace_context=err,
  143. )