docs: def sync_mptt_tree_fields_from_draft_to_published(
          draft_copy, dry_run=False, force_update_cached_urls=False):
      """
      Sync tree structure changes from a draft publishable object to its
      published copy, and updates the published copy's Fluent cached URLs when
      necessary. Or simulates doing this if ``dry_run`` is ``True``.
  
      Syncs both actual structural changes (i.e. different parent) and MPTT's
      fields which are a cached representation (and may or may not be correct).
      """
      mptt_opts = getattr(draft_copy, '_mptt_meta', None)
      published_copy = getattr(draft_copy, 'publishing_linked', None)
      if not mptt_opts or not published_copy:
          return {}
      # Identify changed values and prepare dict of changes to apply to DB
      parent_changed = draft_copy.parent != published_copy.parent
      update_kwargs = {
          mptt_opts.parent_attr: draft_copy._mpttfield('parent'),
          mptt_opts.tree_id_attr: draft_copy._mpttfield('tree_id'),
          mptt_opts.left_attr: draft_copy._mpttfield('left'),
          mptt_opts.right_attr: draft_copy._mpttfield('right'),
          mptt_opts.level_attr: draft_copy._mpttfield('level'),
      }
      # Strip out DB update entries for unchanged or invalid tree fields
      update_kwargs = dict(
          (field, value) for field, value in update_kwargs.items()
          if getattr(draft_copy, field) != getattr(published_copy, field)
          # Only parent may be None, never set tree_id/left/right/level to None
          and not (field != 'parent' and value is None)
      )
  
      change_report = []
      for field, new_value in update_kwargs.items():
          old_value = getattr(published_copy, field)
          change_report.append((draft_copy, field, old_value, new_value))
  
      # Forcibly update MPTT field values via UPDATE commands instead of normal
      # model attr changes, which MPTT ignores when you `save`
      if update_kwargs and not dry_run:
          type(published_copy).objects.filter(pk=published_copy.pk).update(
              **update_kwargs)
  
      # If real tree structure (not just MPTT fields) has changed we must
      # regenerate the cached URLs for published copy translations.
      if parent_changed or force_update_cached_urls:
          # Make our local published obj aware of DB change made by `update`
          published_copy.parent = draft_copy.parent
          # Regenerate the cached URLs for published copy translations.
          change_report += \
              update_fluent_cached_urls(published_copy, dry_run=dry_run)
  
      return change_report