U
    Lk7gn                     @   s   d dl Z d dlZd dlZd dlZd dlZd dlmZmZmZ d dl	m
Z
 d dlmZmZmZ d dlmZ d dlmZmZ dZe jdd	 ZG d
d dZG dd deZG dd deZG dd deZdS )    N)SONDBRefObjectId)UPDATE_OPERATORS)BaseDictBaseListEmbeddedDocumentList_import_class)DeprecatedErrorValidationError)	BaseFieldComplexBaseFieldObjectIdFieldGeoJsonBaseFieldc               	   g   s6   z| D ]}|  qdV  W 5 | D ]}|   q"X dS )zContext manager for temporarily disabling a Field's auto-dereferencing
    (meant to be used from no_dereference context manager)N)_decr_no_dereference_context_incr_no_dereference_context)fieldsfield r   ;/tmp/pip-unpacked-wheel-n1etwkgt/mongoengine/base/fields.py_no_dereference_for_fields   s    

r   c                
   @   s   e Zd ZdZdZdZdZe Z	dZ
dZd/ddZdd	 Zed
d Zedd Zdd Zdd Zedd Zdd Zdd Zd0ddZdd Zdd Zd1d d!Zd"d# Zd2d$d%Zd&d' Zd(d) Zed*d+ Zd,d- Zej d.d+ ZdS )3r   zA base class for fields in a MongoDB document. Instances of this class
    may be added to subclasses of `Document` to define a document's schema.
    NFr   c                 K   s*  |s|nd| _ |p|| _|| _t|p&|| _|| _|| _|| _|| _|	| _	|
| _
d| _d| _| j dk	rzt| j tsztdt| j trd| j ksd| j ks| j drtdtt| t|@ }|rtd	| jjd
|f | j| | j dkrtj| _t jd8  _ntj| _t jd7  _dS )a  
        :param db_field: The database field to store this field in
            (defaults to the name of the field)
        :param required: If the field is required. Whether it has to have a
            value or not. Defaults to False.
        :param default: (optional) The default value for this field if no value
            has been set, if the value is set to None or has been unset. It can be a
            callable.
        :param unique: Is the field value unique or not (Creates an index).  Defaults to False.
        :param unique_with: (optional) The other field this field should be
            unique with (Creates an index).
        :param primary_key: Mark this field as the primary key ((Creates an index)). Defaults to False.
        :param validation: (optional) A callable to validate the value of the
            field. The callable takes the value as parameter and should raise
            a ValidationError if validation fails
        :param choices: (optional) The valid choices
        :param null: (optional) If the field value can be null when a default exists. If not set, the default value
            will be used in case a field with a default value is set to None. Defaults to False.
        :param sparse: (optional) `sparse=True` combined with `unique=True` and `required=False`
            means that uniqueness won't be enforced for `None` values (Creates an index). Defaults to False.
        :param **kwargs: (optional) Arbitrary indirection-free metadata for
            this field can be supplied as additional keyword arguments and
            accessed as attributes of the field. Must not conflict with any
            existing attributes. Common metadata includes `verbose_name` and
            `help_text`.
        Z_idNTzdb_field should be a string.. $zrfield names cannot contain dots (".") or null characters ("\0"), and they must not start with a dollar sign ("$").z%s already has attribute(s): %s,    )db_fieldrequireddefaultbooluniqueunique_withprimary_key
validationchoicesnullsparse_owner_document_BaseField__auto_dereference
isinstancestr	TypeError
startswith
ValueErrorsetdir	__class____name__join__dict__updater   auto_creation_countercreation_counter)selfr   r   r    r"   r#   r$   r%   r&   r'   r(   kwargs	conflictsr   r   r   __init__2   sJ    (

zBaseField.__init__c                 C   s
   || _ d S N)r*   r9   valuer   r   r   set_auto_dereferencing   s    z BaseField.set_auto_dereferencingc                 C   s   t | jdsd| j_| jjS )Nno_dereference_contextr   )hasattr_thread_local_storagerA   r9   r   r   r   _no_dereference_context_local   s    z'BaseField._no_dereference_context_localc                 C   s
   | j dkS )Nr   )rE   rD   r   r   r   _no_dereference_context_is_set   s    z(BaseField._no_dereference_context_is_setc                 C   s   | j d | j_d S Nr   rE   rC   rA   rD   r   r   r   r      s    z&BaseField._incr_no_dereference_contextc                 C   s   | j d | j_d S rG   rH   rD   r   r   r   r      s    z&BaseField._decr_no_dereference_contextc                 C   s   | j o| j S r=   )r*   rF   rD   r   r   r   _auto_dereference   s    zBaseField._auto_dereferencec                 C   s   |dkr| S |j | jS )z=Descriptor for retrieving a value from a field in a document.N)_datagetname)r9   instanceownerr   r   r   __get__   s    zBaseField.__get__c                 C   s   |dkr2| j rd}n| jdk	r2| j}t|r2| }|jrz0| j|jkpT|j| j |k}|rf|| j W n  tk
r   || j Y nX td}t	||rt
||_n.t	|ttfr|D ]}t	||rt
||_q||j| j< dS )z:Descriptor for assigning a value to a field in a document.NEmbeddedDocument)r'   r    callable_initialisedrL   rJ   Z_mark_as_changed	Exceptionr
   r+   weakrefproxyZ	_instancelisttuple)r9   rM   r?   Zvalue_has_changedrP   vr   r   r   __set__   s0    


zBaseField.__set__ c                 C   s    |r|n| j }t|||ddS )zRaise a ValidationError.)errors
field_nameN)rL   r   )r9   messager[   r\   r   r   r   error   s    zBaseField.errorc                 C   s   |S )3Convert a MongoDB-compatible type to a Python type.r   r>   r   r   r   	to_python   s    zBaseField.to_pythonc                 C   s
   |  |S )3Convert a Python type to a MongoDB-compatible type.)r`   r>   r   r   r   to_mongo   s    zBaseField.to_mongoTc                 C   s<   | j jj}i }d|kr||d< d|kr.||d< | j |f|S )z2Helper method to call to_mongo with proper inputs.r   use_db_field)rb   __code__co_varnames)r9   r?   rc   r   Zf_inputsZex_varsr   r   r   _to_mongo_safe_call   s    
zBaseField._to_mongo_safe_callc                 C   s   |t kr| | |S )z:Prepare a value that is being used in a query for PyMongo.)r   validater9   opr?   r   r   r   prepare_query_value   s    
zBaseField.prepare_query_valuec                 C   s   dS )zPerform validation on a value.Nr   )r9   r?   cleanr   r   r   rg      s    zBaseField.validatec                    s   t d}t d}| j}ttt|ttfr:dd |D }t ||frnt fdd|D s| d|  n>t ttfr n g}t	t
|t
| r| dt|  d S )	NDocumentrP   c                 S   s   g | ]\}}|qS r   r   ).0k_r   r   r   
<listcomp>   s     z/BaseField._validate_choices.<locals>.<listcomp>c                 3   s   | ]}t  |V  qd S r=   )r+   )rm   cr?   r   r   	<genexpr>  s     z.BaseField._validate_choices.<locals>.<genexpr>zValue must be an instance of %szValue must be one of %s)r
   r&   r+   nextiterrV   rW   anyr^   lenr0   r,   )r9   r?   rl   rP   Zchoice_listvaluesr   rr   r   _validate_choices   s    zBaseField._validate_choicesc              
   K   s   | j r| | | jd k	rt| jr|z$| |}|d k	rFtd| j W q tk
rx } z| t| W 5 d }~X Y qX nt	d| j | j
|f| d S )Nzlvalidation argument for `%s` must not return anything, it should raise a ValidationError if validation failsz2validation argument for `"%s"` must be a callable.)r&   ry   r%   rQ   r   rL   r   r^   r,   r/   rg   )r9   r?   r:   retexr   r   r   	_validate
  s$    



"zBaseField._validatec                 C   s   | j S r=   r)   rD   r   r   r   owner_document&  s    zBaseField.owner_documentc                 C   s
   || _ d S r=   r}   r9   r~   r   r   r   _set_owner_document*  s    zBaseField._set_owner_documentc                 C   s   |  | d S r=   )r   r   r   r   r   r~   -  s    )
NFNFNFNNFF)rZ   NN)TN)T)!r3   
__module____qualname____doc__rL   
_geo_indexZ	_auto_gen	threadinglocalrC   r8   r7   r<   r@   propertyrE   rF   r   r   rI   rO   rY   r^   r`   rb   rf   rj   rg   ry   r|   r~   r   setterr   r   r   r   r   "   sT             
Z


	$



r   c                       sx   e Zd ZdZd fdd	Zedd Z fddZ fd	d
Zdd Z	dddZ
dd Zdd Zdd Zdd Z  ZS )r   a  Handles complex fields, such as lists / dictionaries.

    Allows for nesting of embedded documents inside complex types.
    Handles the lazy dereferencing of a queryset by lazily dereferencing all
    items in a list / dict rather than one at a time.
    Nc                    s>   |d k	r&t |ts&td| jj d|| _t jf | d S )Nz-field argument must be a Field instance (e.g z(StringField())))r+   r   r-   r2   r3   r   superr<   )r9   r   r:   r2   r   r   r<   :  s    zComplexBaseField.__init__c                C   s   t d }|||| |d}|S )NZDeReference)	max_depthrM   rL   r	   )rM   rL   
ref_valuesr   Z_dereferenceZ	documentsr   r   r   _lazy_load_refsB  s    
z ComplexBaseField._lazy_load_refsc                    sj   t d} jr\t j|r\t|ttfr< fdd|D }n t|tr\ fdd| D }t ||S )N	EnumFieldc                    s   g | ]} j |qS r   r   r`   )rm   Zsub_valrD   r   r   rp   S  s     z,ComplexBaseField.__set__.<locals>.<listcomp>c                    s   i | ]\}}| j |qS r   r   )rm   keysubrD   r   r   
<dictcomp>U  s      z,ComplexBaseField.__set__.<locals>.<dictcomp>)	r
   r   r+   rV   rW   dictitemsr   rY   )r9   rM   r?   r   r   rD   r   rY   M  s    
zComplexBaseField.__set__c           
         s  |dkr| S t d}t d}t d}|j| j j}|oN| jdkpNt| j||f}|jr|r|j| jrt	|j| j dds|j| j}| j
||| jdd|j| j< t|j| j drd	|j| j _t ||}	t|	ttfr8tt| |rt|	tst|	|| j}	nt|	ts*t|	|| j}	|	|j| j< n2t|	trjt|	tsjt|	|| j}	|	|j| j< |r|jrt|	ttfr|	js| j
|	|| jdd}	d	|	_|	|j| j< |	S )
z3Descriptor to automatically dereference references.NReferenceFieldGenericReferenceFieldEmbeddedDocumentListField_dereferencedFr   )r   rM   rL   r   T)r
   _fieldsrL   rI   r   r+   rR   rJ   rK   getattrr   rB   r   r   rO   rV   rW   
issubclasstyper   r   r   r   )
r9   rM   rN   r   r   r   Zauto_dereferencedereferencer   r?   r   r   r   rO   Y  sr    
       zComplexBaseField.__get__c           	         sL  t |tr|S t|dr | S td}t ||r6|S d}t|dsxzd}dd t|D }W n tk
rv   | Y S X  jr j j	  fdd|
 D }n~td	}i }|
 D ]h\}}t ||r|jd
krވ d | }t||j||< qt|dr| ||< q |||< q|rHdd t|
 tddD S |S )r_   r`   BaseDocumentFr   Tc                 S   s   i | ]\}}||qS r   r   )rm   idxrX   r   r   r   r     s      z.ComplexBaseField.to_python.<locals>.<dictcomp>c                    s   i | ]\}}| j |qS r   r   rm   r   itemrD   r   r   r     s     rl   NJYou can only reference documents once they have been saved to the databasec                 S   s   g | ]\}}|qS r   r   rm   ro   rX   r   r   r   rp     s    z.ComplexBaseField.to_python.<locals>.<listcomp>r   r   )r+   r,   rB   r`   r
   	enumerater-   r   r@   rI   r   pkr^   _get_collection_namer   sortedoperator
itemgetter)	r9   r?   r   is_list
value_dictrl   rn   rX   
collectionr   rD   r   r`     sJ    







zComplexBaseField.to_pythonTc                    s  t d}t d}t d}t|tr&|S t|drpt||rF| |S |j}| }t||rl|j|d< |S d}	t|dszd}	d	d
 t|D }W n tk
r   | Y S X j	rԇ fdd
|
 D }
ni }
|
 D ]\}}t||rR|jdkr
d t|di }|d}|s8| ||
|< n| }t||j|
|< qt|dr|j}| }t|||fr|j|d< ||
|< q| |
|< q|	rdd t|

 tddD S |
S )ra   rl   rP   r   rb   _clsFr   Tc                 S   s   i | ]\}}||qS r   r   )rm   rn   rX   r   r   r   r     s      z-ComplexBaseField.to_mongo.<locals>.<dictcomp>c                    s"   i | ]\}}|j | qS r   )r   rf   r   r   r9   rc   r   r   r     s    Nr   Z_metaallow_inheritancec                 S   s   g | ]\}}|qS r   r   r   r   r   r   rp     s    z-ComplexBaseField.to_mongo.<locals>.<listcomp>r   r   )r
   r+   r,   rB   rb   r2   r3   r   r-   r   r   r   r^   r   rK   r   r   r   r   r   )r9   r?   rc   r   rl   rP   r   clsvalr   r   rn   rX   metar   r   r   r   r   rb     sb    









zComplexBaseField.to_mongoc                 C   s   i }| j rt|dr| }nt|}|D ]v\}}z| j | W q* tk
rr } z|jp\|||< W 5 d}~X Y q* ttfk
r } z|||< W 5 d}~X Y q*X q*|r| j j	j
}| jd| d| d|d | jr|s| d dS )z/If field is provided ensure the value is valid.r   NzInvalid z item ())r[   z%Field is required and cannot be empty)r   rB   r   r   r|   r   r[   r/   AssertionErrorr2   r3   r^   r   )r9   r?   r[   sequencern   rX   r^   Zfield_classr   r   r   rg     s"    



zComplexBaseField.validatec                 C   s
   |  |S r=   rb   rh   r   r   r   rj   "  s    z$ComplexBaseField.prepare_query_valuec                 C   s   | j r| j |S d S r=   )r   lookup_member)r9   member_namer   r   r   r   %  s    zComplexBaseField.lookup_memberc                 C   s   | j r|| j _|| _d S r=   )r   r~   r)   r   r   r   r   r   *  s    z$ComplexBaseField._set_owner_document)N)TN)r3   r   r   r   r<   staticmethodr   rY   rO   r`   rb   rg   rj   r   r   __classcell__r   r   r   r   r   2  s   

;2
Er   c                   @   s0   e Zd ZdZdd Zdd Zdd Zdd	 Zd
S )r   z+A field wrapper around MongoDB's ObjectIds.c                 C   s0   zt |tst|}W n tk
r*   Y nX |S r=   )r+   r   rS   r>   r   r   r   r`   3  s    
zObjectIdField.to_pythonc              
   C   sR   t |tr|S ztt|W S  tk
rL } z| t| W 5 d }~X Y nX d S r=   )r+   r   r,   rS   r^   )r9   r?   er   r   r   rb   ;  s    
zObjectIdField.to_mongoc                 C   s   |d kr|S |  |S r=   r   rh   r   r   r   rj   D  s    z!ObjectIdField.prepare_query_valuec                 C   s4   zt t| W n tk
r.   | d Y nX d S )NzInvalid ObjectID)r   r,   rS   r^   r>   r   r   r   rg   I  s    zObjectIdField.validateN)r3   r   r   r   r`   rb   rj   rg   r   r   r   r   r   0  s
   	r   c                       sr   e Zd ZdZejZdZd fdd	Zdd Z	ddd	Z
dd
dZdd Zdd ZdddZdd Zdd Z  ZS )r   z0A geo json field storing a geojson style object.ZGeoBaseTc                    s(   d| j  | _|sd| _t j|| dS )zq
        :param bool auto_index: Automatically create a '2dsphere' index.            Defaults to `True`.
        z%sFieldFN)_type_namer   r   r<   )r9   Z
auto_indexargsr:   r   r   r   r<   V  s    zGeoJsonBaseField.__init__c                 C   s   t |trjt| ddhkrT|d | jkrF| | j d| j d | |d S | d| j  dS n"t |tt	fs| d| j  dS t
| d| j  }||}|r| | dS )	z.Validate the GeoJson object based on its type.r   coordinatesz type must be ""z@%s can only accept a valid GeoJson dictionary or lists of (x, y)Nz"%s can only accept lists of [x, y]z_validate_%s)r+   r   r0   keysr   r^   r   rg   rV   rW   r   lower)r9   r?   rg   r^   r   r   r   rg   `  s$    
zGeoJsonBaseField.validatec              	   C   s   t |ttfsdS z|d d d  W n ttfk
r@   Y dS X g }|D ]>}| |d}|sr|d |d krrd}|rJ||krJ|| qJ|r|rdd| S d	d| S d S )
Nz)Polygons must contain list of linestringsr   z:Invalid Polygon must contain at least one valid linestringFr   z0LineStrings must start and end at the same pointzInvalid Polygon:
%sr   %sr+   rV   rW   r-   
IndexError_validate_linestringappendr4   r9   r?   	top_levelr[   r   r^   r   r   r   _validate_polygonv  s"    z"GeoJsonBaseField._validate_polygonc              	   C   s   t |ttfsdS z|d d  W n ttfk
r<   Y dS X g }|D ]$}| |}|rF||krF|| qF|r|rdd| S dd| S dS )zValidate a linestring.z1LineStrings must contain list of coordinate pairsr   z8Invalid LineString must contain at least one valid pointzInvalid LineString:
%sr   r   Nr+   rV   rW   r-   r   _validate_pointr   r4   r   r   r   r   r     s    
z%GeoJsonBaseField._validate_linestringc                 C   s^   t |ttfsdS t|dks*dt| S t |d ttfrNt |d ttfsZdt| S dS )zValidate each set of coordsz)Points must be a list of coordinate pairs   z*Value (%s) must be a two-dimensional pointr   r   z.Both values (%s) in point must be float or intN)r+   rV   rW   rw   reprfloatintr>   r   r   r   r     s     z GeoJsonBaseField._validate_pointc              	   C   s   t |ttfsdS z|d d  W n ttfk
r<   Y dS X g }|D ]$}| |}|rF||krF|| qF|r~dd| S d S )Nz"MultiPoint must be a list of Pointr   z8Invalid MultiPoint must contain at least one valid pointr   r   r   )r9   r?   r[   Zpointr^   r   r   r   _validate_multipoint  s    
z%GeoJsonBaseField._validate_multipointc              	   C   s   t |ttfsdS z|d d d  W n ttfk
r@   Y dS X g }|D ]&}| |d}|rJ||krJ|| qJ|r|rdd| S dd| S d S )Nz,MultiLineString must be a list of LineStringr   zBInvalid MultiLineString must contain at least one valid linestringFzInvalid MultiLineString:
%sr   r   r   )r9   r?   r   r[   Z
linestringr^   r   r   r   _validate_multilinestring  s    z*GeoJsonBaseField._validate_multilinestringc              	   C   s   t |ttfsdS z|d d d d  W n ttfk
rD   Y dS X g }|D ]&}| |d}|rN||krN|| qN|rdd| S d S )Nz&MultiPolygon must be a list of Polygonr   z<Invalid MultiPolygon must contain at least one valid PolygonFzInvalid MultiPolygon:
%sr   )r+   rV   rW   r-   r   r   r   r4   )r9   r?   r[   Zpolygonr^   r   r   r   _validate_multipolygon  s    z'GeoJsonBaseField._validate_multipolygonc                 C   s$   t |tr|S td| jfd|fgS )Nr   r   )r+   r   r   r   r>   r   r   r   rb     s    
zGeoJsonBaseField.to_mongo)T)T)T)T)r3   r   r   r   pymongoZ	GEOSPHEREr   r   r<   rg   r   r   r   r   r   r   rb   r   r   r   r   r   r   P  s   



r   )
contextlibr   r   rT   r   Zbsonr   r   r   Zmongoengine.base.commonr   Zmongoengine.base.datastructuresr   r   r   Zmongoengine.commonr
   Zmongoengine.errorsr   r   __all__contextmanagerr   r   r   r   r   r   r   r   r   <module>   s(   
      