o
    j`d                     @   s   d Z ddlZddlZddlmZ ddlmZmZmZ ddl	m
Z
mZmZmZmZmZmZ ddlmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZ ddlmZ dZG dd deZ dd Z!G dd dej"#eZ$dS )z5
A custom manager for working with trees of objects.
    N)groupby)connectionsmodelsrouter)FIntegerFieldManyToManyFieldMaxOuterRefQSubquery)gettext)cached_field_value)CantDisableUpdatesInvalidMove)TreeQuerySet)
node_moved)_get_tree_model)TreeManagerc                   @   s   e Zd ZdZe ZdS )SQCountz,(SELECT count(*) FROM (%(subquery)s) _count)N)__name__
__module____qualname__templater   output_field r   r   L/var/www/mitschrieb_seite/venv/lib/python3.10/site-packages/mptt/managers.pyr      s    
r   c                    s   t   fdd}|S )z;
    Delegate method calls to base manager, if exists.
    c                    s4   | j rt| j  j|i |S  | g|R i |S N)_base_managergetattrr   selfargskwargsmethodr   r   wrapped(   s   z!delegate_manager.<locals>.wrapped)	functoolswraps)r%   r&   r   r$   r   delegate_manager#   s   r)   c                       s  e Zd ZdZ fddZ fddZdd ZdXd	d
ZdXddZe	j
dd Ze	j
dd Zedd Zedd Zedd Zedd Zedd Zdd ZedYddZedYd d!Zd"d# Zdi fd$d%Ze	&			'dZd(d)Ze	'd[d*d+Zd\d,d-Zed.d/ Zed0d1 Zed2d3 Zd'e_ed4d5 Z ed]d6d7Z!d^d9d:Z"dYd;d<Z#d=d> Z$d?d@ Z%dAdB Z&d_dDdEZ'dFdG Z(dHdI Z)dYdJdKZ*dLdM Z+dNdO Z,dPdQ Z-dRdS Z.dTdU Z/dVdW Z0  Z1S )`r   z6
    A manager for working with trees of objects.
    c                    sP   t  || |jjs"t|| _d | _| jr$| j|ur&| jj| _d S d S d S d S r   )supercontribute_to_class_metaabstractr   
tree_modelr   _tree_manager)r!   modelname	__class__r   r   r+   7   s   
zTreeManager.contribute_to_classc                    s   t  j|i || j| jS )zO
        Ensures that this manager always returns nodes in tree order.
        )r*   get_querysetorder_bytree_id_attr	left_attrr    r2   r   r   r4   B   s   zTreeManager.get_querysetc                    s  | j |j u sJ |j j t }|rdnd}d| }d| }|dkr( j} j}	n
|dkr2 j} j}	 j}
|	 d| }| d| }| j j jj j j j|	| jgdd	  j	D R  }|si| 
 S t| fd
ddD ]}d}t|d D ]k}t| jt| jt| jt||	t||f\}}}}}|du r|d }||d}q}||kr||d k r||d< ||d kr||d< |d }q}||kr|tdi |
|||d ||d iO }||d}|d }q}|tdi |
|||d ||d iO }qs| |S )a  
        Returns a queryset containing either the descendants
        ``direction == desc`` or the ancestors ``direction == asc`` of a given
        queryset.

        This function is not meant to be called directly, although there is no
        harm in doing so.

        Instead, it should be used via ``get_queryset_descendants()`` and/or
        ``get_queryset_ancestors()``.

        This function works by grouping contiguous siblings and using them to create
        a range that selects all nodes between the range, instead of querying for each
        node individually. Three variables are required when querying for ancestors or
        descendants: tree_id_attr, left_attr, right_attr. If we weren't using ranges
        and our queryset contained 100 results, the resulting SQL query would contain
        300 variables. However, when using ranges, if the same queryset contained 10
        sets of contiguous siblings, then the resulting SQL query should only contain
        30 variables.

        The attributes used to create the range are completely
        dependent upon whether you are ascending or descending the tree.

        * Ascending (ancestor nodes): select all nodes whose right_attr is greater
          than (or equal to, if include_self = True) the smallest right_attr within
          the set of contiguous siblings, and whose left_attr is less than (or equal
          to) the largest left_attr within the set of contiguous siblings.

        * Descending (descendant nodes): select all nodes whose left_attr is greater
          than (or equal to, if include_self = True) the smallest left_attr within
          the set of contiguous siblings, and whose right_attr is less than (or equal
          to) the largest right_attr within the set of contiguous siblings.

        The result is the more contiguous siblings in the original queryset, the fewer
        SQL variables will be required to execute the query.
        e ltgtascdesc__c                 S   s   g | ]}| d qS )-)lstrip).0fr   r   r   
<listcomp>   s    z7TreeManager._get_queryset_relatives.<locals>.<listcomp>c                    s   t |  jt |  jd fS )N_id)r   r6   parent_attr)noptsr   r   <lambda>   s   
z5TreeManager._get_queryset_relatives.<locals>.<lambda>)keyN   )minmaxrL   rM   r   )r0   
_mptt_metar   r7   
right_attrr6   r5   rE   onlyorder_insertion_bynoner   listr   filter)r!   queryset	directioninclude_selffiltersr8   max_opmin_opmax_attrmin_attrtree_keymin_keymax_keyqgroupnext_lftnodetreelftrghtmin_valmax_valmin_maxr   rG   r   _get_queryset_relativesL   s   %








z#TreeManager._get_queryset_relativesFc                 C      |  |d|S )z
        Returns a queryset containing the descendants of all nodes in the
        given queryset.

        If ``include_self=True``, nodes in ``queryset`` will also
        be included in the result.
        r=   rj   r!   rU   rW   r   r   r   get_queryset_descendants      z$TreeManager.get_queryset_descendantsc                 C   rk   )z
        Returns a queryset containing the ancestors
        of all nodes in the given queryset.

        If ``include_self=True``, nodes in ``queryset`` will also
        be included in the result.
        r<   rl   rm   r   r   r   get_queryset_ancestors   ro   z"TreeManager.get_queryset_ancestorsc              	   c   s    | j jjrtd| j j | j jjrtd| j j | j| j ur)td| j j | j js2dV  dS | j d zdV  W | j d dS | j d w )a0  
        Context manager. Disables mptt updates.

        NOTE that this context manager causes inconsistencies! MPTT model
        methods are not guaranteed to return the correct results.

        When to use this method:
            If used correctly, this method can be used to speed up bulk
            updates.

            This doesn't do anything clever. It *will* mess up your tree.  You
            should follow this method with a call to ``TreeManager.rebuild()``
            to ensure your tree stays sane, and you should wrap both calls in a
            transaction.

            This is best for updates that span a large part of the table.  If
            you are doing localised changes (one tree, or a few trees) consider
            using ``delay_mptt_updates``.

            If you are making only minor changes to your tree, just let the
            updates happen.

        Transactions:
            This doesn't enforce any transactional behavior.  You should wrap
            this in a transaction to ensure database consistency.

        If updates are already disabled on the model, this is a noop.

        Usage::

            with transaction.atomic():
                with MyNode.objects.disable_mptt_updates():
                    ## bulk updates.
                MyNode.objects.rebuild()
        zBYou can't disable/delay mptt updates on %s, it's an abstract modelz`You can't disable/delay mptt updates on %s, it's a proxy model. Call the concrete model instead.zOYou can't disable/delay mptt updates on %s, it doesn't contain the mptt fields.NFT)	r0   r,   r-   r   r   proxyr.   _mptt_updates_enabled_set_mptt_updates_enabledr!   r   r   r   disable_mptt_updates   s2   
&

z TreeManager.disable_mptt_updatesc              	   c   s    |   B | jjrdV  n)| j  zdV  W n ty%   | j   w | j }| j}|D ]}|| q0W d   dS W d   dS 1 sJw   Y  dS )a  
        Context manager. Delays mptt updates until the end of a block of bulk
        processing.

        NOTE that this context manager causes inconsistencies! MPTT model
        methods are not guaranteed to return the correct results until the end
        of the context block.

        When to use this method:
            If used correctly, this method can be used to speed up bulk
            updates.  This is best for updates in a localised area of the db
            table, especially if all the updates happen in a single tree and
            the rest of the forest is left untouched.  No subsequent rebuild is
            necessary.

            ``delay_mptt_updates`` does a partial rebuild of the modified trees
            (not the whole table).  If used indiscriminately, this can actually
            be much slower than just letting the updates occur when they're
            required.

            The worst case occurs when every tree in the table is modified just
            once.  That results in a full rebuild of the table, which can be
            *very* slow.

            If your updates will modify most of the trees in the table (not a
            small number of trees), you should consider using
            ``TreeManager.disable_mptt_updates``, as it does much fewer
            queries.

        Transactions:
            This doesn't enforce any transactional behavior.  You should wrap
            this in a transaction to ensure database consistency.

        Exceptions:
            If an exception occurs before the processing of the block, delayed
            updates will not be applied.

        Usage::

            with transaction.atomic():
                with MyNode.objects.delay_mptt_updates():
                    ## bulk updates.
        N)ru   r0   _mptt_is_tracking_mptt_start_tracking	Exception_mptt_stop_trackingpartial_rebuild)r!   resultsrz   tree_idr   r   r   delay_mptt_updates  s&   
-




"zTreeManager.delay_mptt_updatesc                 C   
   | j jjS r   )r0   rN   rE   rt   r   r   r   rE   \     
zTreeManager.parent_attrc                 C   r~   r   )r0   rN   r7   rt   r   r   r   r7   `  r   zTreeManager.left_attrc                 C   r~   r   )r0   rN   rO   rt   r   r   r   rO   d  r   zTreeManager.right_attrc                 C   r~   r   )r0   rN   r6   rt   r   r   r   r6   h  r   zTreeManager.tree_id_attrc                 C   r~   r   )r0   rN   
level_attrrt   r   r   r   r   l  r   zTreeManager.level_attrc           
      K   s^   i }dj }| D ]#\}}|d}g }|j}|D ]}	|t| |	d |	 q||||< q	|S )Nr>   _attr)joinitemssplitappendr   )
r!   lookupsnew_lookups
join_partskvparts	new_partsnew_parts__appendpartr   r   r   _translate_lookupsp  s   
zTreeManager._translate_lookupsNc                 K   (   |du r| }|j di | jdi |S )zg
        Like ``self.filter()``, but translates name-agnostic filters for MPTT
        fields.
        Nr   )rT   r   )r!   qsrX   r   r   r   _mptt_filter|  s   zTreeManager._mptt_filterc                 K   r   )zS
        Like ``self.update()``, but translates name-agnostic MPTT fields.
        Nr   )updater   )r!   r   r   r   r   r   _mptt_update  s   zTreeManager._mptt_updatec                 K   s   t tj| jfi | S r   )r   r   db_for_writer0   )r!   hintsr   r   r   _get_connection  s   zTreeManager._get_connectionc                 C   s   |r|d t | j|d t | j|d t | ji}n'|}|dD ]}	|j|	}
|
j}q!|
}t|t	r7d}n|j
j}|t |i}|jjdi ||d}|jdi |t|iS )a  
        Adds a related item count to a given ``QuerySet`` using its
        ``extra`` method, for a ``Model`` class which has a relation to
        this ``Manager``'s ``Model`` class.

        Arguments:

        ``rel_model``
           A ``Model`` class which has a relation to this `Manager``'s
           ``Model`` class.

        ``rel_field``
           The name of the field in ``rel_model`` which holds the
           relation.

        ``count_attr``
           The name of an attribute which should be added to each item in
           this ``QuerySet``, containing a count of how many instances
           of ``rel_model`` are related to it through ``rel_field``.

        ``cumulative``
           If ``True``, the count will be for each item and all of its
           descendants, otherwise it will be for each item itself.

        ``extra_filters``
           Dict with aditional parameters filtering the related queryset.
        	__tree_id
__lft__gte
__lft__lter>   pkNr   )r
   r6   r7   rO   r   r,   	get_fieldrelated_model
isinstancer   remote_field
field_nameobjectsrT   valuesannotater   )r!   rU   	rel_model	rel_field
count_attr
cumulativeextra_filterssubquery_filterscurrent_rel_modelrel_field_partcurrent_mptt_field
mptt_fieldr   subqueryr   r   r   add_related_count  s&   $
zTreeManager.add_related_count
last-childTc                 C   s  |j r|s| j|j d rttd|du r@|  }t|| jd t|| jd t|| j	d t|| j
| t|| jd n| r|dv r|rN|  t|| j
}|dkr_|}|d }	n|d }|}	| |	 t|| jd t|| jd t|| j	d t|| j
| t|| jd n]t|| jd t|| j	d |r|  | |||\}	}
}}}t|| j
}| d|	| t|| j|  t|| j| d  t|| j	|
  t|| j
| t|| j| |r| || |r|  |S )	a  
        Sets up the tree state for ``node`` (which has not yet been
        inserted into in the database) so it will be positioned relative
        to a given ``target`` node as specified by ``position`` (when
        appropriate) it is inserted, with any necessary space already
        having been made for it.

        A ``target`` of ``None`` indicates that ``node`` should be
        the last root node.

        If ``save`` is ``True``, ``node``'s ``save()`` method will be
        called before it is returned.

        NOTE: This is a low-level method; it does NOT respect
        ``MPTTMeta.order_insertion_by``.  In most cases you should just
        set the node's parent and let mptt call this during save.
        r   z2Cannot insert a node which has already been saved.NrK      r   leftrightr   )r   rT   exists
ValueError__get_next_tree_idsetattrr7   rO   r   r6   rE   is_root_node_mptt_refreshr   _create_tree_space!_calculate_inter_tree_move_values_create_space'_post_insert_update_cached_parent_rightsave)r!   rc   targetpositionr   allow_existing_pkrefresh_targetr|   target_tree_idspace_targetlevelr   parentright_shiftr   r   r   insert_node  s^   

zTreeManager.insert_nodec                 C   s   | j jr| j||||d|dS |d u r | r| | d S d S | r1|dv r1| ||| d S | r>| ||| d S | ||| d S )NT)r   r   r   r   r   )	r.   rv   r   is_child_node_make_child_root_noder   _make_sibling_of_root_node_move_root_node_move_child_node)r!   rc   r   r   r   r   r   r   r   
_move_node)  s$   	zTreeManager._move_nodec                 C   s0   | j |||d |  tj|j|||d dS )ax  
        Moves ``node`` relative to a given ``target`` node as specified
        by ``position`` (when appropriate), by examining both nodes and
        calling the appropriate method to perform the move.

        A ``target`` of ``None`` indicates that ``node`` should be
        turned into a root node.

        Valid values for ``position`` are ``'first-child'``,
        ``'last-child'``, ``'left'`` or ``'right'``.

        ``node`` will be modified to reflect its new tree state in the
        database.

        This method explicitly checks for ``node`` being made a sibling
        of a root node, as this is a special case due to our use of tree
        ids to order root nodes.

        NOTE: This is a low-level method; it does NOT respect
        ``MPTTMeta.order_insertion_by``.  In most cases you should just
        move the node yourself by setting node.parent.
        )r   )senderinstancer   r   N)r   r   r   sendr3   )r!   rc   r   r   r   r   r   	move_nodeC  s
   

zTreeManager.move_nodec                 C   s   | j |dd S )zF
        Returns the root node of the tree with the given id.
        N)r|   r   )r   get)r!   r|   r   r   r   	root_node`     zTreeManager.root_nodec                 C   s   | j ddS )z?
        Creates a ``QuerySet`` containing root nodes.
        Nr   )r   rt   r   r   r   
root_nodesg  s   zTreeManager.root_nodesc                 C   s`   | j j}| jdd}|jr|j|j }|jddd}| j}d}|D ]}|d7 }||d| q!dS )zO
        Rebuilds all trees in the database table using `parent` link.
        Nr   r   Tflatr   rK   )r0   rN   r   rQ   r5   values_list_rebuild_helper)r!   rH   r   pksrebuild_helperidxr   r   r   r   rebuildn  s   zTreeManager.rebuildc                 C   sl   | j j}| jd|d}|jr|j|j }|jddd}|sdS t|dkr+td| | |d d| dS )	z
        Partially rebuilds a tree i.e. It rebuilds only the tree with given
        ``tree_id`` in database table using ``parent`` link.
        N)r   r|   r   Tr   rK   zKMore than one root node with tree_id %d. That's invalid, do a full rebuild.r   )	r0   rN   r   rQ   r5   r   lenRuntimeErrorr   )r!   r|   rH   r   r   r   r   r   rz     s   zTreeManager.partial_rebuildc                    s   j j |rC|j|dv r't| j}|dkrt| j}n-t| jd }n$t| jd }|dkr<t| jd }nt| j}n d}d}g d fdd	|||d |rnd	t	 |d  S )ad  
        Load a tree from a nested dictionary for bulk insert, returning an
        array of records. Use to efficiently insert many nodes within a tree
        without an expensive `rebuild`.

        ::

            records = MyModel.objects.build_tree_nodes({
                'id': 7,
                'name': 'parent',
                'children': [
                    {
                        'id': 8,
                        'parent_id': 7,
                        'name': 'child',
                        'children': [
                            {
                                'id': 9,
                                'parent_id': 8,
                                'name': 'grandchild',
                            }
                        ]
                    }
                ]
            })
            MyModel.objects.bulk_create(records)

        r   r   rK   first-childr   c                    s   t | } | dg }jdi | }| t| j t| j| t| j| |D ]}||d |d d}q.|d7 }t| j| |S )NchildrenrK   cursorr   r   )	dictpopr0   r   r   r6   r   r7   rO   )datar   r   r   rc   childrH   r!   stackr|   treeifyr   r   r     s   
z-TreeManager.build_tree_nodes.<locals>.treeifyr   r   N)rK   r   )
r0   rN   r|   r   r   r7   rO   r   r   r   )r!   r   r   r   r   r   r   r   r   build_tree_nodes  s*   zTreeManager.build_tree_nodesr   c                 C   s   | j j}|d }| j|d}|jr|j|j }|jddd}| j}	|D ]}
|	|
|||d }q#| j j| j	j
|d}| j|||||d |d S )NrK   )
parent__pkr   Tr   r   )r   r   r   r|   )r0   rN   r   rQ   r5   r   r   _default_manager
db_managerdbrT   r   )r!   r   r   r|   r   rH   r   r   	child_idsr   child_idr   r   r   r     s   zTreeManager._rebuild_helperc                 C   sb   t || jt|| j|  t|| j}|r/|st }|| ||v r%t| j|||d d S d S )N)seen)	r   rO   r   r   rE   setaddr   r   )r!   r   r   r   r   r   r   r   r     s   
z3TreeManager._post_insert_update_cached_parent_rightc                 C   s   t || j}t || j}t || j}t || j}t || j}|dks&|dkr:|dkr/|d }	n|}	|| d }
|}n&|dksB|dkrX|dkrK|d }	n|}	|| }
t || j}nttd| ||	 d }d}|rrd| d  }|	|
|||fS )	z~
        Calculates values required when moving ``node`` relative to
        ``target`` as specified by ``position``.
        r   r   rK   r   r   "An invalid position was given: %s.r   r   )r   r7   r   rO   rE   r   r   get_descendant_count)r!   rc   r   r   r   r   target_lefttarget_righttarget_levelr   level_changer   left_right_changer   r   r   r   r     s.   

z-TreeManager._calculate_inter_tree_move_valuesc                 C   s   |  | || dS )z
        Closes a gap of a certain ``size`` after the given ``target``
        point in the tree identified by ``tree_id``.
        N_manage_spacer!   sizer   r|   r   r   r   
_close_gap%  s   zTreeManager._close_gapc                 C   s   |  ||| dS )z
        Creates a space of a certain ``size`` after the given ``target``
        point in the tree identified by ``tree_id``.
        Nr  r  r   r   r   r   ,  r   zTreeManager._create_spacerK   c                 C   s:   | j |d}| j|t| j| d | j|d | dS )zt
        Creates space for a new tree by incrementing all tree ids
        greater than ``target_tree_id``.
        )tree_id__gt)r|   rK   N)r   r   r   r6   r.   _mptt_track_tree_insertions)r!   r   	num_treesr   r   r   r   r   3  s   zTreeManager._create_tree_spacec                 C   s,   t | t| j d }|pd}|d S )zj
        Determines the next largest unused tree id for the tree managed
        by this manager.
        r   rK   )rS   	aggregater	   r6   r   )r!   max_tree_idr   r   r   r   <  s   zTreeManager._get_next_tree_idc                 C   s   | j |d}|jj}| jj}d|| jjj||| jj	||| j
j	||| jj	||| jj	d }t|| j
}	t|| j}
|
|	 d }|	d }|	|
||	|
||	|
||||	|
|||t|| jg}| }||| dS )z
        Removes ``node`` from its current tree, with the given set of
        changes being applied to ``node`` and its descendants, closing
        the gap left by moving ``node`` as it does so.
        r   aU  
        UPDATE %(table)s
        SET %(level)s = CASE
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                    THEN %(level)s - %%s
                ELSE %(level)s END,
            %(tree_id)s = CASE
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                    THEN %%s
                ELSE %(tree_id)s END,
            %(left)s = CASE
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                    THEN %(left)s - %%s
                WHEN %(left)s > %%s
                    THEN %(left)s - %%s
                ELSE %(left)s END,
            %(right)s = CASE
                WHEN %(right)s >= %%s AND %(right)s <= %%s
                    THEN %(right)s - %%s
                WHEN %(right)s > %%s
                    THEN %(right)s - %%s
                ELSE %(right)s END
        WHERE %(tree_id)s = %%s)tabler   r   r|   r   rK   N)r   ops
quote_namer0   r,   r.   db_tabler   r   columnr7   r6   rO   r   r   execute)r!   rc   r   r   new_tree_id
connectionqnrH   inter_tree_move_queryr   r   gap_sizegap_target_leftparamsr   r   r   r   _inter_tree_move_and_close_gapE  sF   
z*TreeManager._inter_tree_move_and_close_gapc                 C   s   t || j}t || j}t || j}|s|  }|d }| |||| t|| j||  t|| j||  t|| jd t|| j| t|| jd d|j	| j< dS )a  
        Removes ``node`` from its tree, making it the root node of a new
        tree.

        If ``new_tree_id`` is not specified a new tree id will be
        generated.

        ``node`` will be modified to reflect its new tree state in the
        database.
        rK   r   N)
r   r7   rO   r   r   r  r   r6   rE   _mptt_cached_fields)r!   rc   r  r   r   r   r   r   r   r   r     s   z!TreeManager._make_child_root_nodec                 C   s  ||kr
t td| jj}t|| j}t|| j}| rV|dkr)|d }|}n|dkr4|}|d }nttd| | | ||krNt	|| j|d  | 
|| dS |dkr||krv| }	||	krhdS t|	| j}||}
}d}n<|}||}
}d}n2|dkr||kr|}||}
}d}n | }||krdS t|| j}||}
}d}nttd| | j|d}|jj}d	|| jjj||| jjd
 }| }||||||
|g t	|| j| dS )a  
        Moves ``node``, making it a sibling of the given ``target`` root
        node as specified by ``position``.

        ``node`` will be modified to reflect its new tree state in the
        database.

        Since we use tree ids to reduce the number of rows affected by
        tree mangement during insertion and deletion, root nodes are not
        true siblings; thus, making an item a sibling of a root node is
        a special case which involves shuffling tree ids around.
        +A node may not be made a sibling of itself.r   rK   r   r   Nr  z
            UPDATE %(table)s
            SET %(tree_id)s = CASE
                WHEN %(tree_id)s = %%s
                    THEN %%s
                ELSE %(tree_id)s + %%s END
            WHERE %(tree_id)s >= %%s AND %(tree_id)s <= %%s)r  r|   )r   r   r0   r,   r   r6   r   r   r   r   r   get_previous_siblingget_next_siblingr   r  r  r.   r  r   r  r   r  )r!   rc   r   r   rH   r|   r   r   r  left_siblinglower_boundupper_boundshiftright_siblingr  r  root_sibling_queryr   r   r   r   r     sj   





z&TreeManager._make_sibling_of_root_nodec           	   
   C   s   | j jr| j | dS |  }|jj}| jj}d|| j jj||	| j
j||	| jj||	| jjd }| }|||||||||g dS )z
        Manages spaces in the tree identified by ``tree_id`` by changing
        the values of the left and right columns by ``size`` after the
        given ``target`` point.
        a  
            UPDATE %(table)s
            SET %(left)s = CASE
                    WHEN %(left)s > %%s
                        THEN %(left)s + %%s
                    ELSE %(left)s END,
                %(right)s = CASE
                    WHEN %(right)s > %%s
                        THEN %(right)s + %%s
                    ELSE %(right)s END
            WHERE %(tree_id)s = %%s
              AND (%(left)s > %%s OR %(right)s > %%s))r  r   r   r|   N)r.   rv   _mptt_track_tree_modifiedr   r  r  r0   r,   r  r   r7   r  rO   r6   r   r  )	r!   r  r   r|   r  r  rH   space_queryr   r   r   r   r    s    zTreeManager._manage_spacec                 C   sD   t || j}t || j}||kr| ||| dS | ||| dS )z
        Calls the appropriate method to move child node ``node``
        relative to the given ``target`` node as specified by
        ``position``.
        N)r   r6   _move_child_within_tree_move_child_to_new_tree)r!   rc   r   r   r|   r   r   r   r   r   !  s
   zTreeManager._move_child_nodec                 C   s   t || j}t || j}t || j}t || j}| |||\}}	}
}}|| d }| ||| | ||	|
| t|| j||
  t|| j||
  t|| j||	  t|| j| t|| j	| |j
|j| j	< dS )a  
        Moves child node ``node`` to a different tree, inserting it
        relative to the given ``target`` node in the new tree as
        specified by ``position``.

        ``node`` will be modified to reflect its new tree state in the
        database.
        rK   N)r   r7   rO   r   r6   r   r   r  r   rE   r   r  )r!   rc   r   r   r   r   r   r  r   r   r   r   new_parent_right
tree_widthr   r   r   r(  /  s,   	z#TreeManager._move_child_to_new_treec                 C   s  t || j}t || j}t || j}|| d }t || j}t || j}	t || j}
t || j}|dks8|dkr||krBttd||	  k rL|k rTn nttd|dkrn|
|kre|
| }|
d }n|
}|
| d }n|	|kr{|	| d }|	}n|	d }|	| }|| d }|}nf|dks|dkr||krttd||	  k r|k rn nttd	|dkr|	|kr|	| }|	d }n|	}|	| d }n|
|kr|
| d }|
}n|
d }|
| }|| }t || j}nttd
| t	||}t
||}|| }|}|dkr
| }| j|d}|jj}| jj}d|| jjj||| jj||| jj||| jj||| jjd }| }||||||||||||||||||g t|| j| t|| j| t|| j||  t|| j| |j|j| j< dS )z
        Moves child node ``node`` within its current tree relative to
        the given ``target`` node as specified by ``position``.

        ``node`` will be modified to reflect its new tree state in the
        database.
        rK   r   r   )A node may not be made a child of itself.9A node may not be made a child of any of its descendants.r   r   r  z;A node may not be made a sibling of any of its descendants.r   r   r  a  
        UPDATE %(table)s
        SET %(level)s = CASE
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                  THEN %(level)s - %%s
                ELSE %(level)s END,
            %(left)s = CASE
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                  THEN %(left)s + %%s
                WHEN %(left)s >= %%s AND %(left)s <= %%s
                  THEN %(left)s + %%s
                ELSE %(left)s END,
            %(right)s = CASE
                WHEN %(right)s >= %%s AND %(right)s <= %%s
                  THEN %(right)s + %%s
                WHEN %(right)s >= %%s AND %(right)s <= %%s
                  THEN %(right)s + %%s
                ELSE %(right)s END
        WHERE %(tree_id)s = %%sr  r   r   r   r|   N)r   r7   rO   r   r6   r   r   rE   r   rL   rM   r   r  r  r0   r,   r.   r  r   r  r   r  r   r   r  )r!   rc   r   r   r   r   r   widthr|   r   r   r   new_left	new_rightr   r   left_boundaryright_boundaryr   r  r  r  rH   move_subtree_queryr   r   r   r   r'  X  s   




z#TreeManager._move_child_within_treec              
   C   s  t || j}t || j}t || j}t || j}t || j}|| d }	||kr.ttd||kr8ttd| |||\}
}}}}| |	|
| | j	|d}|j
j}| jj}d|| jjj||| jj||| jj||| jj||| jjd }| }|||||||||g t|| j||  t|| j||  t|| j||  t|| j| t|| j| |j|j| j< dS )z
        Moves root node``node`` to a different tree, inserting it
        relative to the given ``target`` node as specified by
        ``position``.

        ``node`` will be modified to reflect its new tree state in the
        database.
        rK   r+  r,  r  a  
        UPDATE %(table)s
        SET %(level)s = %(level)s - %%s,
            %(left)s = %(left)s - %%s,
            %(right)s = %(right)s - %%s,
            %(tree_id)s = %%s
        WHERE %(left)s >= %%s AND %(left)s <= %%s
          AND %(tree_id)s = %%sr-  N)r   r7   rO   r   r6   r   r   r   r   r   r  r  r0   r,   r.   r  r   r  r   r  r   rE   r   r  )r!   rc   r   r   r   r   r   r|   r  r.  r   r   r   r   r   r  r  rH   move_tree_queryr   r   r   r   r     sb   	
zTreeManager._move_root_node)Fr   )r   FFT)r   TT)r   )Nr   )r   )rK   )2r   r   r   __doc__r+   r4   rj   rn   rp   
contextlibcontextmanagerru   r}   propertyrE   r7   rO   r6   r   r   r)   r   r   r   r   r   r   r   r   r   r   alters_datarz   r   r   r   r   r  r   r   r   r  r   r   r  r   r(  r'  r   __classcell__r   r   r2   r   r   1   s    

t



I
=




		
>X





H
$
		
EW#) r   )%r5  r6  r'   	itertoolsr   	django.dbr   r   r   django.db.modelsr   r   r   r	   r
   r   r   django.utils.translationr   r   mptt.compatr   mptt.exceptionsr   r   mptt.querysetsr   mptt.signalsr   
mptt.utilsr   __all__r   r)   Managerfrom_querysetr   r   r   r   r   <module>   s     $	