U
    8[Se4                    @   sn  d dl Z d dlZd dlmZmZ d dlmZ d dlmZ d dlmZ d dl	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 d d	lmZ d d
lmZ d dl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%m&Z&m'Z' ddl(m)Z)m*Z*m+Z+m,Z, ddl-m.Z.m/Z/m0Z0m1Z1 ddl2m3Z3 ddl4m5Z5m6Z6 G dd de.Z7G dd dZ8dS )    N)defaultdict
namedtuple)copy)List)OrderedDict)IntegrityError)
ForeignKeyfuncinspect)json)Config)command)UNIQUE_VIOLATION)errors)close_all_sessions)
CmfRelBaseCmfType)fields)models   )CmfOrmErrorCmfOrmUniqueErrorCmfOrmIntegrityError)BaseModelMetaDEFAULT_DATASOURCE	BaseModelCmfGM2MModel   )BaseDataDriver
BaseMapperMapperinc_select_count)imutable_deep_copy)cache_obj_lock_release_allemit_delayed_eventsc                	       s|  e Zd ZdZi Ze Zejj	j
eedZe ZdZ fddZedd Zedd	 Zed
d ZedqddZdrddZdd Zdd Zdd Zdd Zedd Zdd Zdd Zdd Zd d! Zed"d# Z ed$d% Z!e"d&d'Z#dsd)d*Z$edte%e# d,d-d.Z&dud/d0Z'ed1d2 Z(ej)dd+fd3d4Z*ed5d6 Z+ed7d8 Z,d9d: Z-edvd;d<Z.dwe#d=d>d?Z/d+d@dAdBZ0ddddd+dd+d(dCdDdEZ1dddFdGdHZ2ddd+dd+dIdJdKZ3dxdLdMZ4dNdO Z5dPdQ Z6d+dRdSdTZ7dUdV Z8d+d@e9dWdXdYZ:ddZd[d\Z; fd]d^Z< fd_d`Z=dadb Z>dcdd Z?dedf Z@dgdh ZAe fdidjZBdkdl ZCdmdn ZDdodp ZE  ZFS )ySQLAlchemyDataDriveru  
    dataproviders['default'] = {
        'type': 'sqlalchemy',
        'sqlachemy.url': 'postgresql://user:password@host/db',
        'sqlachemy.echo': True,  # Достаточно для отладки запросов в develop mode
    }

    # Для отладки в продакшене логгеры:
    sqlalchemy.engine - controls SQL echoing. set to
        logging.INFO for SQL query output, logging.
        DEBUG for query + result set output.
        These settings are equivalent to echo=True and echo="debug" on create_engine.echo, respectively.
    sqlalchemy.pool - controls connection pool logging.
         set to logging.INFO to log connection invalidation and recycle events;
         set to logging.DEBUG to additionally log all pool checkins and checkouts.
         These settings are equivalent to pool_echo=True and pool_echo="debug" on create_engine.echo_pool, respectively.
    sqlalchemy.dialects - controls custom logging for SQL dialects, to the extend that logging
         is used within specific dialects, which is generally minimal.
    sqlalchemy.orm - controls logging of various ORM functions to the extent that logging
         is used within the ORM, which is generally minimal.
         Set to logging.INFO to log some top-level information on mapper configurations.

    Example:
        logging.basicConfig()
        logging.getLogger('sqlalchemy.engine').setLevel(logging.INFO)
    )Zclass_registryZmetadataNc                    sT   t  j|| tj| jfdd | D | _tjtjj	| jd| _
|   d S )Nc                 S   s*   i | ]"\}}| d r|d dd|qS )zsqlalchemy. r   )
startswithreplace).0kv r,   "./cmf/data_providers/sqlalchemy.py
<dictcomp>G   s   
 z1SQLAlchemyDataDriver.__init__.<locals>.<dictcomp>)Zbind)super__init__
sqlalchemyZengine_from_configconfigitemsengineormZscoped_sessionZsessionmakerSession	init_metaselfargskwargs	__class__r,   r-   r0   B   s    zSQLAlchemyDataDriver.__init__c                 C   s  g }|j  D ]\}}t|tr"q|js0|js0qd |g}i }|jrJd|d< |jr|jr|jD ]}|dkr||tj	|| q\|dkr|tj	d|j
 d| d| |fd|did	| q\|tj	d|j
 d| d| |fd
|i| q\q|tj	|| q|S )NTuniquedefaultZgin_trgmZix__ZginZgin_trgm_ops)postgresql_usingZpostgresql_opsrA   )r   r3   
issubclassr   indexr>   Zindex_usingappendr1   ZIndex	tablename)cls	cmf_modelZindexescmf_field_name	cmf_fieldr:   r;   Zidx_typer,   r,   r-   Z__get_indexesR   sJ    

 
	
z"SQLAlchemyDataDriver.__get_indexesc                 C   s    | j |}|r| j | |S N)cached_queriesgetmove_to_end)rF   keyretr,   r,   r-   	get_query|   s    zSQLAlchemyDataDriver.get_queryc                 C   s   || j |< | jsZtdtd d d }|d }|d }|dk rHd}|dkrTd}|| _t| j | jkr| j jdd\}}td	|  d S )
NSC_PAGE_SIZESC_PHYS_PAGESi   i         F)ZlastzDEV: set_query evicted key )rK   cached_queries_limitossysconflenpopitemprint)rF   queryrN   Zsys_memsize_mbZchunk_countZqueries_limitZevicted_keyr@   r,   r,   r-   	set_query   s    
zSQLAlchemyDataDriver.set_queryc                 C   s  |j }|jrdS t|tjr(t }nt|tjr>tj}nt|tj	r^t
|j|j}nt|tjr|t|jg}|jr|t|j tj||j|jdS t|tjrt }n>t|tjrt|j}n"t|tjrdS t|tj rdS t|tjrdS t|tjr dS t|tjr2dS t|tjrNtjdd}nt|tj rft! }nt|tj"r~t }n~t|tj#rdS t|tj$rdS t|tj%rtj&}nDt|tj'rtj(}n.t|tj)rddl*m+} |}nt,d| tj|||j-|j|jdS )	u0   Создадим SA поле для моделиN)primary_keynullableT)timezoner   )TSVECTORu"   Не найден тип поля )r?   r^   r]   ).
class_nameZvirtualrB   r   Z	CmfBigIntr1   ZBIGINTCmfIntZIntegerZ
CmfNumericZNumericZ	precisionZscaleCmfTUUIDStringZ
max_lengthZforeign_keyrD   r   ZColumnr]   r^   ZCmfTextZTEXTZCmfStrCmfRelationCmfGenericRelationZCmfGenericM2MZCmfDateRangeZCmfM2MZCmfDateTime	TIMESTAMPZCmfDateZDATEZCmfJson
CmfBackrefCmfGenericBackrefCmfBoolZBooleanZCmfTimeZTimeZCmfTsVectorZsqlalchemy.dialects.postgresqlr`   	Exceptionr?   )rF   Z
_cmf_modelrI   Z_db_field_namecolumn_nameZsa_typer:   r`   r,   r,   r-   _make_sa_column   sr    
  

z$SQLAlchemyDataDriver._make_sa_columnc                 C   s   t | jj||d}|S )N)schema)r
   r4   Zget_columns)r9   
table_namern   columnsr,   r,   r-   inspect_table_columns   s    z*SQLAlchemyDataDriver.inspect_table_columnsc                 C   s>   |  d |j}|  }|d| d| d|  |  d S )Nzalter table z add column  )rm   typer6   executecommit)r9   ro   rl   Zcmf_field_typeZsa_column_typesr,   r,   r-   add_custom_column   s    z&SQLAlchemyDataDriver.add_custom_columnc              
   C   s   dddd}t jt jdddd|d< t jt jdddd|d	< t jt jd
dd|d< t jt jddd|d< t|tf|}tj|j	j
}|  }z|| |  W n@ tk
r } z"|j  td| d|  W 5 d}~X Y nX dS )u5   
        Создаем М2М таблицу
        Tcustom.r&   abstract
__module____qualname__u   ID Объекта)captionr^   rC   left_idu   ID Элементаright_id   Имя объекта)r}   r^   Zleft_name_cacheu   Имя элементаZright_name_cache-   Ошбика создания таблицы : N)r   Fieldrc   	CmfStr256rs   r   r1   rn   CreateTabledp_model	__table__r6   rt   ru   rk   transactionrollbackrZ   )r9   
model_namer;   rG   smtprv   er,   r,   r-   add_custom_m2m_model   s    

z)SQLAlchemyDataDriver.add_custom_m2m_modelc              	   C   s   dddd}t jt jddddddd|d	< t jt jd
dd|d< t jt jdddddd|d< t jt jdddddd|d< t jt jddddd|d< t jt jdddddd|d< t|tf|}|S )NTrx   r&   ry   u)   Идентификатор объектаu3   Автоматически генерируетсяF)r}   commentr^   r]   readonlyvisibleidr   )r}   rC   nameu   Сортировкаr   )r}   rC   r   r^   r?   Zordernou   Кодu3   Код в реальном мире из жизни)r}   r>   r   r   r^   codeu:   Код родителя в каскадном выборе)r}   r   r^   rC   Zchoice_parent_idu   СкрытьZ
cmf_hidden)	r   r   rc   r   rb   ZCmfStr64rj   rs   r   )r9   r   r;   rG   r,   r,   r-   gen_custom_choice_model  s`    
	




	z,SQLAlchemyDataDriver.gen_custom_choice_modelc              
   C   s~   |  |}tj|jj}|  }z|| |  W n@ t	k
rx } z"|j
  td| d|  W 5 d}~X Y nX dS )ur   
        Создаем пользовательский справочник выбора таблицу
        r   r   N)r   r1   rn   r   r   r   r6   rt   ru   rk   r   r   rZ   )r9   r   rG   r   rv   r   r,   r,   r-   add_custom_choice_model5  s    


z,SQLAlchemyDataDriver.add_custom_choice_modelc                 C   s   |j }| j|}|s|j}g }g }ddi}|j D ].\}}	|}
| ||	|
}|dkrZq6|| q6|| 	| || |t
|d}|dd |D  t|| jf|}|| j|< |S )u]   
        Вернём SA модель для CMF модели.
        TODO: db_name
        Zextend_existingTN)Z__tablename____table_args__c                 S   s   i | ]}|j |qS r,   r   )r)   cr,   r,   r-   r.   f  s      z5SQLAlchemyDataDriver.dp_model_cls.<locals>.<dictcomp>)__name__models_registryrL   rE   r   r3   rm   rD   extend"_SQLAlchemyDataDriver__get_indexestupleupdaters   sa_base_model)rF   rG   r   sa_modelrE   Z
sa_columnsr   Z__table_kwargs__rH   rI   Zsa_field_nameZ	sa_columnZsa_model_kwargsr,   r,   r-   dp_model_clsD  s0    
z!SQLAlchemyDataDriver.dp_model_clsc                 C   s
   |  |S rJ   )r   )r9   rG   r,   r,   r-   r   k  s    zSQLAlchemyDataDriver.dp_modelc                 C   s   t t|jS )uG   
        Вернём CMF модель для SA модели.
        )getattrr   r   )r9   r   r,   r,   r-   rG   n  s    zSQLAlchemyDataDriver.cmf_modelc                 C   s   t |tjS rJ   )
isinstancer%   r   )r9   Zdp_instancer,   r,   r-   is_instancet  s    z SQLAlchemyDataDriver.is_instancec                 C   s0   i }|D ]}|j j||j jj< qtj|d|S )N
table_type)r   r   fullnamer1   r5   polymorphic_union)r9   Z
cmf_modelsaliasZtablesmodelr,   r,   r-   _union_modelsy  s    z"SQLAlchemyDataDriver._union_modelsc                 C   sX   d}| d |kr&| d | d | d fS | d |krH| d | d | d fS t d| |d S )N)><==>=<==IN!=><<>NOT INLIKENOT LIKEILIKE	NOT ILIKEz
SIMILAR TOzNOT SIMILAR TOEXISTS
NOT EXISTS	MEMBER_OF@@r   r   r   zInvalid filter operation)r   )paramsZop_listr,   r,   r-   _parse_params  s    z"SQLAlchemyDataDriver._parse_paramsc                 C   s  ddl m}m} t|tjr:|jdkr2||jd |j}nXt|t	rt	 }|D ]>}t|tjr|jdkrt||jd |
|j qN|
| qN|}|dkrd }| dkr||kS | dkr||kS | dkr||k S | d	kr||kS | d
kr||kS | dkr ||kS | dkr||S | dkr(||S | dkr<||S | dkrR|| S | dkrf||S | dkr||| S | dkr||S | dkr|| S || |S d S )Nr   print_debugr2   .u<   collect_filter_exp: Значение не загруженоZNULLr   r   r   r   )r   r   )r   r   r   r   r   r   r   r   r   r   r   )cmf.includer   r2   r   r   r   _valueDEBUGvaluelistrD   in_ZlikeZilikeop)operfieldvalr   r2   new_valr+   r,   r,   r-   _expression  sZ    
















z SQLAlchemyDataDriver._expressionJoinDatazJleft_table, right_table, alias, left_field_name, right_model, right_modelsTc                 C   s
  |r|j }|j}nT|j|}|s.| || t|tjtjfrLt|tjrft	d|j  d|j  d|j  d}|r| d| n|}|
 }|s|rt	d| d|j  d|j  dd S |d	 }	t|d
krtjj|	jj|d}
n| ||}
| ||
|||	|}|S )NuX   Недопустимый тип поля для вложенной фильтрации .up   . Вложенная фильтрация возможно только для CmfRelation и CmfGenericRelationZ_sub__uj   Не возможно построить запрос для вложенной фильтрации по u   , т.к. у поля u#    не указаны related_modelsr   r   r   )ra   instance_classr   rL   _raise_invalid_fieldrB   re   rf   CmfSubclassedGenericRelationr   related_modelsrX   r1   r5   Zaliasedr   r   r   r   )r9   r   
left_table
field_name
from_aliasis_models_required	field_clsZ
alias_partmodels_right_modelright_table	join_datar,   r,   r-   _calc_join_data  s6    
z$SQLAlchemyDataDriver._calc_join_dataF)joinsc                 C   sb   |r
t  ndd | j D }|D ]:}|j|kr2q"| |jt|jj|j	 d|jjj
k} q"| S )uP  
        TODO cmf_deleted
        Все данные для необходимых присоединений в joins, добавим недостающие присоединения в запрос.
        froms могут быть от филдов, при этом без джойна, чтобы принудительно создать join нужен force = True.
        Или более продвинутый способ определения наличия нужного джойна.
        Сейчас при построении основного запроса, джойны принудительно добавляются.
        А при расчёте фильтров, когда нет алиасов от полей, джойн делается только если нет нужного алиаса.
        c                 S   s"   h | ]}t |tjjjr|jqS r,   )r   r1   sql
selectableAliasr   )r)   r   r,   r,   r-   	<setcomp>  s   z3SQLAlchemyDataDriver._make_joins.<locals>.<setcomp>_id)set	statementlocate_all_fromsr   Z	outerjoinr   r   r   r   Zleft_field_namer   )r[   r   forceZavailable_joinsr   r,   r,   r-   _make_joins  s    

z SQLAlchemyDataDriver._make_joinsc                 C   sd   d }g }|d krd }|j j}n|j}|dD ]0}| ||||}|| |j}|j}|j}q.|S )Nr   )	r   r   r   splitr   rD   r   r   r   )r9   	join_pathr   r   r   r   r   r   r,   r,   r-   _calc_joins_by_path  s    

z(SQLAlchemyDataDriver._calc_joins_by_pathc                 C   s0   t |jdd}t|  d|j d| d S )N,z,
u+    не существует у модели u   , но есть: 
)strr   r(   r   ra   )r   r   Zfields_r,   r,   r-   r     s    z)SQLAlchemyDataDriver._raise_invalid_fieldc           .   
   C   s  |s|d fS t |d trbg }|D ]2}	| j|	|||||d\}}
|
d krJq"||
 q"||| fS |d dkr| j|dd  |||||dS |d dkr| j|dd  |||tj||dS |d dkr t|dks|d d	krtd
|||d }|d fS | 	|\}}}t
t|tr&|j}t
t|tr>|jj}t |ttfrg }|D ]N}t
t|trx||j n*t
t|tr||jj n
|| qV|}d|kr|d\}}}| j|||d}| ||}|d }|j}|j}d}n|d kr|jj}d}|j|}|dkr<|dkr<|jd}|sXtd| d|j |d krn| || t
|tjr |dkrt |tst |tstd||||d	kr|d k	rtd||||dkr>| j|d||d}|d krtd| d|jr| t j!}|d	krR|j"#|$|j%|k|j&|jk' }|| fS |j"#|$|j%|k|j&|jk| (d|j)|' }n|* }| |}|j+r|j,}|j-}n"|j.r|j-}|j,}ntd|||d	kr |j"#|$||k' }|| fS |j"#|$||k| (d||' }||dkr6|n| fS |dkr| j|d||d}|d krttd| d | j/| d d d |d!}|j}|j}|j"#|j0j} |jr| t j!}!|!j}"| 1|"|!j)|j0jk} | $|!j%|k|!j&|jk} nh|j.rd"}#d#}$nd#}#d"}$|* }| |}!|!j}"t2|!|#}%t2|!|$}&| 1|"|&|j0jk} | $|%|k} t3|j0d$rx|sxd$d%dg}'ng }'t3|j0d&r|'d&d%dgg}'|r|'|g}'| j|'||j| ||d\} }'| $|'} | ' } |4d'r|  } || fS td(|||n"t
|tj5tj6fr@|dkr<|4d'r.d)nd*}dd|g}|dkrTtd+|||| j|d||d}|d krtd| d,| j/| d d d |d!}(|(j})|(j}*|j7 d-}+t2|*j0|+},|j"#|,} |+d%|g}'t3|*j0d$r|'d$d%dgg}'|r|'|g}'| j|'|)|)j| |*|d\} }'| $|'} | ' } |4d'r8|  } || fS t
|tj8r"| d-}|d.kr"| j/| d d d |d!}|j}|j}|j"#|j0j} | t j!}!|!j}"| 1|"|!j)|j0jk} | $t| (d|!j%|| (d|!j9||!j&d/k} | j||||d}|t| (d||| (d|| fS |dkrxt j:j;|j||ddd0gd1}-|-sTd2g}-| j|d||d}|| (d||-fS | j||||d}|d krtd| d3| d4|| (|||fS d S )5Nr   )field_tableinclude_deletedZANDr   OR)cmdr   r   order_by   )r   r   ut   Операция order_by в фильтре должна быть в формате: ["order_by", "=", [поля..]]r   r   )r   TFANYr   r   u	   Поле u    не найдено у )r   r   u   Правое значение для IN, NOT IN для m2m должно быть list, т.е. в квадратных скобках, например "field", "IN", "[obj.id]"u   Недопустимая операция над m2m полем (сравнение = только на None), допустимые IN, NOT IN, = None)r   r   r   r   
with_aliasu   В таблице u3    нет поля id для фильтрации m2mr   uD   Надо указать для поля либо left либо right)r   r   uG    нет поля id для фильтрации в подзапросе)r   r~   r   cmf_deletedr   is_dummyzNOT uh   Недопустимая операция над m2m полем, допустимые IN, NOT IN, = Noner   r   ul   Недопустимая операция над backref полем, допустимые EXISTS, NOT EXISTSu7    нет поля id для фильтрации backrefr   r   Z
rg_membersi'  )r   r   Zsearch_queryZonly_idssliceZNOTFOUNDu    нет поля u    для фильтрации)<r   r   collect_filter_exprD   r1   or_rX   rk   order_queryr   rB   rs   r   r   r   r   r   
rpartitionr   r   r   r   r   r   r   rL   r   ra   r   
CmfM2MBase_query_columnZnested_fieldsr   ZRelationCachesessionr[   filterZ	parent_idZparent_fieldexistsr   Zchild_idm2m_model_clsrightr   r~   leftr   r   joinr   hasattrr'   rh   ri   backrefCmfRelationBaseZparent_codeZCmfFullSearchZfulltext_search).r9   Z	in_filterr   r   r[   r   r   r   Z
filter_resr+   Zexpr   r  r  Z_rightir   r@   r   r   r   r   Zfield_column	m2m_modelZm2m_subqueryZdp_m2m_modelZdp_m2m_field_selfZdp_m2m_field_theirZself_columnZsub_dataZ	sub_modelZ	sub_tableZ	sub_queryZm2m_sa_modelZ	m2m_tablemy_id_field_namesub_id_field_namemy_sa_fieldsub_sa_fieldZsub_filter_expZbackref_dataZbackref_modelZbackref_tableZbackref_column_nameZbackref_columnZ	found_idsr,   r,   r-   r     s   
      


"      















     

   


  

     






z'SQLAlchemyDataDriver.collect_filter_expc                 C   s
   |  dS )NZ_m2m_directr,   r   r,   r,   r-   _m2m_depth_alias_name  s    z*SQLAlchemyDataDriver._m2m_depth_alias_namec                 C   s
   |  dS )NZ_m2m_idr,   r  r,   r,   r-   _m2m_alias_name  s    z$SQLAlchemyDataDriver._m2m_alias_namec                 C   sh   | dg }t|tr t|}|r8t|d ts8|g}| D ]"\}}||jkr@||d|g q@|S )u   
        Преобразуем обычные kwargs в продвинутый фильтр для однообразной логики фильтрования
        :param model:
        :param kwargs:
        :return:
        r  r   r   )	rL   r   r   r   loadsr   r3   r   rD   )r9   r   r;   smart_filterr*   r+   r,   r,   r-   _get_filter  s    


z SQLAlchemyDataDriver._get_filterc                 C   sl   |r4|j  D ]$}t|tjjjr| j|jkr|} qt| j	|d}|dk	rh|rd|
t|ddS |S dS )u   Для полей "основной" таблицы не нужен префикс таблицы, для этого нужно указать with_alias=FalseNr   r   )r   r   r   r1   r   r   r   r   r   r   Zlabelr   r(   )tablerl   r[   r   Zfrom_columnr,   r,   r-   r  1  s    z"SQLAlchemyDataDriver._query_columnparent_joinc                    s   |r
|j n|jj}| D ]\}| j|t|d}|dk	rJ|| q|r|r\|jp`g n|g}	 fdd|	D }
|
s|q|
d   j}|r|j	nd}| j
|||dd}|sq|| | j||||j|d qdS )	u2   Соберём поля и таблицы по fflr   Nc                    s>   g | ]6}|j   rt t jt jfrt t js qS r,   )r   rL   rB   re   rf   r   )r)   Zrelated_modelr   r   r,   r-   
<listcomp>H  s
   z?SQLAlchemyDataDriver._select_joined_columns.<locals>.<listcomp>r   F)r   r  )r   r   r   r3   r  boolrD   Zright_modelsr   r   r   _select_joined_columnsr   )r9   Zfflr   rp   r   r  r  Zsub_fflr  r   Z
fields_clsZ
left_modelr   r   r,   r  r-   r  =  s4        
z+SQLAlchemyDataDriver._select_joined_columnsr   c                O   s.  |  |}|  }ddlm} |jd|j |d}	t| |	}
|
rZ|
 }
|
	|}
nht

 }g }g }| |||| |j| }
| j|
|dd}
t

 | dkr|
 	d }t| ||	 t|dkr|j}tt|D ]}|| ||| < q| ||}|r*| j||||
|d\}
}|
|}
|
S )	Nr   	CMF_CACHEzquery: full_fields_loadT)r   g{Gz?r   )r   r6   r   r"  hashr   rs   rP   Z_cloneZwith_sessiontimer  r[   r   r\   rX   r]   ranger  r   r  )r9   r   r$  r   r:   r;   r   rv   r"  rN   r[   Zstart_calc_timer   rp   Zclone_qpkeyr  r  
filter_expr,   r,   r-   _create_query[  s4    


z"SQLAlchemyDataDriver._create_query)r   r$  r   group_by
for_updatemapperr   load_m2mc                O   s   t |t }t|}| |
}| |}| j|f||d| }|j|||d}|rd|j| }|rp|	 }|
 }|j|||d}|r|	r| j||||d |r|  |S )   Грубо селектим всю таблицу, фильтры:
          - в kwargs: поле -> значение
        r$  r   r   r$  r   r   )r   r   
get_mappermap_args
map_kwargsr*  with_labelsr   r   with_for_updatealllist_to_cmf	_load_m2mpost_mapping_hook)r9   r   r   r$  r   r+  r,  r-  r   r.  r:   r;   mapper_is_my_argsqZsql_resZres_objsr,   r,   r-   r     s:    


 

  zSQLAlchemyDataDriver.list)r$  r+  c                   s
  |  |}| |}| j|fd|i| }|r| |  fdd|D }|jj|jj |t	
 fj| d}	t|j|	}
g }|
D ]<}i }t|D ]\}}|| ||< q|d |d< || q|S |jj|jj t	
 gd}	|j|	 }|S )r/  r$  c                    s   g | ]} j j| qS r,   )r   r   )r)   rl   r   r,   r-   r    s     z.SQLAlchemyDataDriver.count.<locals>.<listcomp>Nr   count)r4  r5  r*  r6  r   r   Zselect_fromZfromsZwith_only_columnsr	   r@  r+  r   r   r  rt   	enumeraterD   Zscalar)r9   r   r$  r+  r:   r;   r=  r>  rp   Zcount_qres_listresrowZrow_resr  rl   r@  r,   r?  r-   r@    s>    


	
"zSQLAlchemyDataDriver.count)r   r$  r,  r-  r   c                O   s   t |t }	t|}| |}| |}| j|f|||d|}
|
 }
|j|
|ddgd}
|rn|
 }
|
	 }|r|j
|||d}| j|g|||d |	r|  |S dS )u   
        Выбрать один объект по фильтру:
          - в args - первичный ключ(опционально)
          - в kwargs - другие поля: поле -> значениеr0  r   r   r1  r2  )r-  r   N)r   r   r3  r4  r5  r*  r6  r   r7  firstobject_to_cmfr:  r;  )r9   r   r   r$  r,  r-  r   r:   r;   r<  r>  sa_instanceZres_objr,   r,   r-   rL     s     


zSQLAlchemyDataDriver.getc           <   
      sN  |r|sdS t |t  fdddd }t|}tt}tt}tt}	|D ]&}
||
 |
 ||
j |
 qN|D ]t}tt|}|D ]`}|| sqt||d}|rt|t	j
rt|t	jr||  qt|t	jt	jfr|	| | qqz| D ]B\}}g }|D ]8}
tt|
j}|j	|}|r
t|t	jr
 rt|
|}|jdk	rt|jdk	r
||j q
d|_|jdkrd|_| }t|
|}|j}|dkrtd||n`t|
|d}|dk	r|r
|| q
t|
|d | }t|
|d}|dkrtd|||r
|dd	 }|| | |
 q
| D ]\}}tt|d}|slqNt  d
dt|g}|st|drdddg|g}t|drdddg|g}| j||| ||d}|D ]H}|}|| D ]0}
 r
t|
|}||_||_nt|
|| qqԐqN|r| j||| ||d q|	 D ] \}}|D ]}|j}|| }t||}t|t	jr|jrt }|j d}|dfdd|D g} tt|| f|i i}!| D ]f}"| }#d|"j	kr|s| dddgg}#d|"j	kr|#dddgg}#| j|"|!|#|d}$|"|$||"j< q|std	dl m!}%m"}& |%|&j#d|j d|j d qTnVt$|dkrt  t%t&|' ( }'n,t  | )t*j+,dd | D d( }'tt}(|'D ]})t|)|}*|(|* |) qg }+|D ]}
|(|
}, rzt|
|}|,sBg |_|jdkrg |_q|-|,|| |_|jdkrlt.|j|_|+/|j n<|,st|
|g  q|-|,|| }t|
|| |+/| q|+rB| j|+|| ||d qTt|t	jrT|j0rd}-d}.nd}-d}.|1 }/| 2|/}0t|0|-}1t|0|.}2| )|1|23|14fd d|D }3tt}4tt}5t  |3D ]@\}6}7|7rh|6sqh|5|7 |6 |4|7dd	  |7 qh|4 D ] \}}8tt|}t  d
d|8g}|st|drdddg|g}t|drdddg|g}| j||| ||dd!}+|+rD| j|+|| ||d |+D ]}
|5|
 D ]t}6||6 D ]d}9 r|9| }:|:jdkrg |:_|:j|
 n0t|9|d}|dkrg }t|9|| ||
 qdqXqHq|D ]f}
 r |
| };|;jdkrg |;_|;jdkrt.|;j|;_|;5  nt|
|ddkrt|
|g  qڐqTqFdS )"u   
        Прогрузка М2М и М2О полей
        :param objs: список обьектов
        :param full_fields_load: поля обьектов
        :return:
        Nc                    s    r| j jS | j S rJ   )r   r   )Zobj_)is_heavyr,   r-   obj_id  s    z.SQLAlchemyDataDriver._load_m2m.<locals>.obj_idc                  S   s   dd } t | S )Nc                   S   s   t tS rJ   )r   r   r,   r,   r,   r-   id_to_list_factory  s    zWSQLAlchemyDataDriver._load_m2m.<locals>.model_to_id_factory.<locals>.id_to_list_factory)r   )rJ  r,   r,   r-   model_to_id_factory  s    z;SQLAlchemyDataDriver._load_m2m.<locals>.model_to_id_factory.u#   Не загружено id поле:r   r   r   r   r   Fr   )r$  r-  r  r   r   c                    s   g | ]} |qS r,   r,   r)   orI  r,   r-   r  r  s     z2SQLAlchemyDataDriver._load_m2m.<locals>.<listcomp>)r$  r  r   r   zORM: skip load field r   z die no related modelsr   c                 S   s   i | ]\}}||j qS r,   )r   )r)   r*   mr,   r,   r-   r.     s      z2SQLAlchemyDataDriver._load_m2m.<locals>.<dictcomp>r   r~   r   c                    s   g | ]} |qS r,   r,   rM  rO  r,   r-   r    s     )r$  r  r-  r.  )6r   r    r   r   rD   ra   r   r   rB   r   r   r  r   ZCmfBackrefBaser3   rL   r   Z_oldZid_fieldnamer   setattr	partitionr!   r	  r:  r
  dictr"   r   r*  r   rE   r   r   r2   r   rX   nextitervaluesr8  r[   r1   r5   r   r9  r   r   r  r  r   r  r   Zapply_changes)<r9   Zobjsr$  r-  r   rK  Zto_load_mappingZmy_objs_by_idZmy_objs_by_model_nameZmodels_by_rel_fieldsobjr   r   r   r   Zmodel_to_idZalready_loaded_obj_listr   Zid_field_nameZid_fieldZid_field_valueZfield_valueZrel_model_nameZid_to_obj_listZ_filterZ	load_objsZload_objZ	loaded_idZmodels_listZobj_listZm2mqueryZbackref_field_nameZfilter_qZ	field_fflZ	rel_modelZmodel_query_filterr>  r   r2   rB  Zres_dictr   Zm2m_idZsub_objsZref_obj_listr  r  r  r   r  r  Z	m2m_queryZid_by_modelsZmy_id_by_sub_idZmy_idZsub_idZsub_id_listZmy_objZmy_fieldZ	obj_fieldr,   )rH  rI  r-   r:    s   









  


 






   

zSQLAlchemyDataDriver._load_m2mc                 C   s6   g }|D ](}t |tjr&||j q|| q|S rJ   )r   r   r   rD   r   )r9   r:   Znew_argsargr,   r,   r-   r4    s    zSQLAlchemyDataDriver.map_argsc                 C   s:   i }|  D ](\}}t|tjr,|j||< q|||< q|S rJ   )r3   r   r   r   r   )r9   r;   Z
new_kwargsr*   r+   r,   r,   r-   r5    s    
zSQLAlchemyDataDriver.map_kwargs)	no_reloadc             
   O   s  |  |}| |}|  }| |j}| }	t }
|
||	 z||	 |j	|	gd W n t
k
r } z4|j  t|jttrt||t||W 5 d}~X Y n0 tk
r } z|j  |W 5 d}~X Y nX d|_|rdS |
j|	|d}|
  d|_|S )um   
        no_reload - не загружать объект из базы и не возвращать
        ZobjectsNT)
cmf_entity)r4  r5  r6   r   r=   r   r3  cmf_to_objectaddflushr   r   r   r   origr   lookupr   r   r   rk   is_newrF  r;  )r9   instancerY  r:   r;   r=  _kwargsrv   r   rG  r-  r   rW  r,   r,   r-   create  s2    





zSQLAlchemyDataDriver.createc              
      s   |  |}| |}|  }|  j}t j}|  t fdd|D }	||	|	}
z|
|
 W n0 tk
r } z|j  |W 5 d}~X Y nX  S )u*   TODO логическое удалениеc                 3   s   | ]}t  |V  qd S rJ   r   r)   frb  r,   r-   	<genexpr>=  s     z.SQLAlchemyDataDriver.delete.<locals>.<genexpr>N)r4  r5  r6   r   r=   rs   r]   r   r[   rL   deleterk   r   r   )r9   rb  r:   r;   r=  rc  rv   r   r(  get_argsrG  r   r,   rh  r-   rj  5  s    



zSQLAlchemyDataDriver.delete)rV  c                O   s`   |  |}|  }||}| ||}	|	rN| j|	||||d\}}
||
}|j|dd}|S )Nr   Zfetch)Zsynchronize_session)r   r6   r[   r  r   r  r   )r9   r   rV  r   r:   r;   r   rv   r[   r  r)  Zupdated_cntr,   r,   r-   bulk_updateF  s    


z SQLAlchemyDataDriver.bulk_updater#  c             
      s.  |  |}| |}|  }| t }t j}|  t fdd|D }|||}	t	
 }
|
 |	 z|j|	gd W n~ tk
r } z0|j  t|jttrt |t |W 5 d }~X Y n2 tk
r } z|j  |W 5 d }~X Y nX  j}|
j|	 |d}|
  ||_|S )Nc                 3   s   | ]}t  |V  qd S rJ   re  rf  rh  r,   r-   ri  Y  s     z.SQLAlchemyDataDriver.update.<locals>.<genexpr>rZ  )r[  r$  )r4  r5  r6   r   rs   r]   r   r[   rL   r   r3  r\  r^  r   r   r   r   r_  r   r`  r   r   r   rk   ra  rF  r;  )r9   rb  r$  r:   r;   rv   r   r(  rk  rG  r-  r   ra  rW  r,   rh  r-   r   S  s0    




zSQLAlchemyDataDriver.updatec                    s   t    d S rJ   )r/   before_requestr9   r<   r,   r-   rm  x  s    z#SQLAlchemyDataDriver.before_requestc                    s   t    d S rJ   )r/   before_first_requestrn  r<   r,   r-   ro  {  s    z)SQLAlchemyDataDriver.before_first_requestc                 C   sB   ddl m} t  |  |  |    | j  t  d S Nr   r!  )	r   r"  r#   cache_unlockZcache_transaction_startr6   ru   remover$   r9   r"  r,   r,   r-   ru   ~  s    
zSQLAlchemyDataDriver.commitc                 C   s:   ddl m} t  |  |    | j  t  d S rp  )r   r"  r#   rq  r6   r   rr  r$   rs  r,   r,   r-   r     s    
zSQLAlchemyDataDriver.rollbackc                 O   s   |   j||S rJ   r6   r[   r8   r,   r,   r-   r[     s    zSQLAlchemyDataDriver.queryc                 O   s   |   j||S rJ   rt  r8   r,   r,   r-   query_deprecated  s    z%SQLAlchemyDataDriver.query_deprecatedc                    s   t  | d S rJ   )r/   make_models)rF   r   r<   r,   r-   rv    s    z SQLAlchemyDataDriver.make_modelsc                 C   s^   t tD ]P}tt|}t|tr|jr(q|jr<| j|jkr<q|jsN| jtkrNq| 	| qdS )u   Создадим схемуN)
dirr   r   r   r   rz   Zdata_sourcesr   r   r   )r9   r   rG   r,   r,   r-   r7     s    
zSQLAlchemyDataDriver.init_metac                 C   sd   |   }|d |  | j| j tjt	 d}t
|}|d| jd  t|d d S )Nz'CREATE EXTENSION IF NOT EXISTS pg_trgm;zalembic.inizsqlalchemy.urlhead)r6   rt   ru   db_metaZ
create_allr4   rV   pathr  getcwdr   Zset_main_optionr2   r   Zstamp)r9   rv   Zalembic_config_fileZalembic_configr,   r,   r-   init_db  s    
zSQLAlchemyDataDriver.init_dbc                 C   s   t   | j  d S rJ   )r   r4   Zdisposern  r,   r,   r-   r     s    z'SQLAlchemyDataDriver.close_all_sessions)N)N)NTN)F)N)NT)N)F)Gr   r{   r|   __doc__r   r1   ZMetaDatary  extZdeclarativeZdeclarative_baser   r   rK   rU   r0   classmethodr   rP   r\   rm   rq   rw   r   r   r   r   r   rG   r   r   staticmethodr   r   r   r   r   r   r   r   r   and_r   r  r  r  r  r  r*  r   r@  rL   r:  r4  r5  rd  rj  rS  rl  r   rm  ro  ru   r   r[   ru  rv  r7   r|  r   __classcell__r,   r,   r<   r-   r%      s    
)

I
3
&


1

 

  

*     $
  $		$%	r%   c                   @   s   e Zd Zdd ZdS )SAModelAccessorc                 C   s   |dkrd S t tt|S )N)Z_pytestfixturefunctionZ__test__)r%   r   r   r   )r9   itemr,   r,   r-   __getattr__  s    zSAModelAccessor.__getattr__N)r   r{   r|   r  r,   r,   r,   r-   r    s   r  )9rV   r&  collectionsr   r   r   typingr   r   Zsqlalchemy.ext.declarativer1   Zsqlalchemy.ormZsqlalchemy.sql.selectableZsqlalchemy.excr   r   r	   r
   Zflaskr   Zalembic.configr   Zalembicr   Zpsycopg2.errorcodesr   Zpsycopg2r   r   Zcmf.fields.base_fieldsr   r   Zcmfr   r   r   Z
base_errorr   r   r   Zmodels.base_modelr   r   r   r   baser   r   r    r!   Zutil.immutablesr"   Zutil.cmfutilr#   r$   r%   r  r,   r,   r,   r-   <module>   sJ              +