U
    Lk7g1                     @   s   d dl mZmZ d dl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mZmZmZ d dlmZ G dd	 d	Zd
S )    )SONDBRef)BaseDictBaseListEmbeddedDocumentListTopLevelDocumentMetaclassget_document)LazyReference)get_db)DocumentEmbeddedDocument)	DictField	ListFieldMapFieldReferenceField)QuerySetc                   @   s4   e Zd ZdddZdddZddd	Zdd
dZdS )DeReference   Nc                    s@  |dkst |tr|S t |tr.dd |D }|| _d|rt |tttfr|j|t	drnj
q\t trjt	|d }|rtfdd|D r|S |stfdd| D r|S js fd	d
 fdd t	|ds|}n |}| || _| jd| _| |d||S )aL  
        Cheaply dereferences the items to a set depth.
        Also handles the conversion of complex data types.

        :param items: The iterable (dict, list, queryset) to be dereferenced.
        :param max_depth: The maximum depth to recurse to
        :param instance: The owning instance used for tracking changes by
            :class:`~mongoengine.base.ComplexBaseField`
        :param name: The name of the field, used for tracking changes by
            :class:`~mongoengine.base.ComplexBaseField`
        :param get: A boolean determining if being called by __get__
        Nc                 S   s   g | ]}|qS  r   .0ir   r   ;/tmp/pip-unpacked-wheel-n1etwkgt/mongoengine/dereference.py
<listcomp>*   s     z(DeReference.__call__.<locals>.<listcomp>fielditemsc                 3   s   | ]}|j  kV  qd S N	__class__r   doc_typer   r   	<genexpr>;   s     z'DeReference.__call__.<locals>.<genexpr>c                 3   s   | ]}|j  kV  qd S r   r   r   r   r   r   r!   =   s    c                    s`   g }| D ]R}|}t |tr$ |}n,t |tr8|}nt |ttfsP|}|| q|S r   )
isinstancedictlistr   r   	to_pythonappend)r   	new_itemsvvalue_get_items_from_dict_get_items_from_listr   r   r   r,   F   s    




z2DeReference.__call__.<locals>._get_items_from_listc                    sf   i }|   D ]T\}}|}t|tr,|}n,t|tr@ |}nt|ttfsX|}|||< q|S r   )r   r"   r$   r#   r   r   r%   )r   r'   kr(   r)   r*   r   r   r+   S   s    





z2DeReference.__call__.<locals>._get_items_from_dictr   r   )r"   strr   	max_depthr   r   r   _fieldsgethasattrr   r   document_typeallvaluesdbref_find_referencesreference_map_fetch_objects
object_map_attach_objects)selfr   r/   instancenameis_listr   )r+   r,   r    r   r   __call__   s@    
 

zDeReference.__call__r   c                 C   s&  i }|r|| j kr|S t|tr*| }n|}|d7 }|D ]}t|ttfrV|j D ]\}}|j	|d}t|t
r~qZqZt|tr||jt |j qZt|ttfrd|kr|t|d t |d j qZt|tttfrZ|| j krZtt|dddd}	| ||}
|
 D ]2\}}t|	ttfr:|	}||t | qqZq:t|t
rfq:q:t|tr||jt |j q:t|ttfrd|kr|t|d t |d j q:t|tttfr:|d | j kr:| ||d }
|
 D ]\}}||t | q q:|S )z
        Recursively finds all db references to be dereferenced

        :param items: The iterable (dict, list, queryset)
        :param depth: The current depth of recursion
        r   N_ref_clsr   r3   )r/   r"   r#   r5   r   r   r0   r   _datar1   r	   r   
setdefaultr3   setaddidr   r   r$   tuplegetattrr7   r   update
collection)r<   r   depthr8   iteratoritem
field_namer   r(   Z	field_cls
referenceskeyrefsr   r   r   r7   i   s\    





   zDeReference._find_referencesc           	   	      sj  i | j  D ]T\}tdddk	}|rt   fdd|D }j|}| D ]\}}| |f< q\qt|ttt	frqfdd|D }|r|
  dd|ii}|D ]}||}||jf< qqt  dd|ii}|D ]j}d|krt|d |}n:|dkrJtd	d
d dD |}n
||}||jf< qqS )z:Fetch all references and convert to their document objectsobjectsNc                    s   g | ]} |fkr|qS r   r   r   r6   )col_namer:   r   r   r      s     z.DeReference._fetch_objects.<locals>.<listcomp>c                    s   g | ]} |fkr|qS r   r   rT   )rK   r:   r   r   r      s     Z_idz$inrB    c                 s   s   | ]}|  V  qd S r   )
capitalize)r   xr   r   r   r!      s     z-DeReference._fetch_objects.<locals>.<genexpr>_)r8   r   rI   Z_get_collection_namerS   Zin_bulkr"   r   r   r   Z_get_dbfind	_from_sonrG   r
   r   joinsplit)	r<   r    ZdbrefsZref_document_cls_existsrR   rP   rQ   docrefr   )rU   rK   r:   r   r9      sH    




zDeReference._fetch_objectsc                 C   s$  |s<t |ttfr|S |r<t |tr0t|||S t|||S t |ttfrd|krp| j|d j|d jf|S d|krt	|d 
|}|jdd}|d= | |j||d|_|dk	r||jd< |S t|dsd}t}t |trt}t |t}	t|}
g }nd}| }
i }|d7 }|
D ]\}}|r:|| n|||< || jkrf|sf| j| ||< qt |ttfrX|jD ]}|| j|d}t |tr| j|j|jf||| j|< nt |ttfrd|kr| j|d j|d jf||| j|< nNt |tttfr||| jkr|| d| d| }| j||||d	|| j|< q|nt |tttfr|| jkr|r| d| n|}| j||d ||d	||< n2t |trt|d
r| j|j|jf|||< q|r|r|r|	r t|S ||||S t|||S |d7 }|S )a  
        Recursively finds all db references to be dereferenced

        :param items: The iterable (dict, list, queryset)
        :param depth: The current depth of recursion
        :param instance: The owning instance used for tracking changes by
            :class:`~mongoengine.base.ComplexBaseField`
        :param name: The name of the field, used for tracking changes by
            :class:`~mongoengine.base.ComplexBaseField`
        rA   rB   Nr   TFr   .)r=   r>   rG   )r"   r   r   r#   r   r:   r1   rK   rG   r   r[   rC   popr;   r2   r   rH   	enumerater   r&   r   r   r0   r   r$   r/   )r<   r   rL   r=   r>   r^   rB   r?   Z	list_typeas_tuplerM   datar-   r(   rO   Z	item_namer   r   r   r;      s    
 




        zDeReference._attach_objects)r   NN)r   )N)r   NN)__name__
__module____qualname__r@   r7   r9   r;   r   r   r   r   r      s   
R
;
-r   N)Zbsonr   r   Zmongoengine.baser   r   r   r   r   Zmongoengine.base.datastructuresr	   Zmongoengine.connectionr
   Zmongoengine.documentr   r   Zmongoengine.fieldsr   r   r   r   Zmongoengine.querysetr   r   r   r   r   r   <module>   s   