U
    <cR                    @   sR  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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.m/Z/m0Z0 ddl1m2Z2 G dd de-Z3G dd dZ4dS )    N)defaultdict
namedtuple)copy)List)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_copyc                       sB  e Zd ZdZi Ze Zejj	j
eedZi Z fddZedd Zedd Zed	d
 ZediddZdjd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dkd%d&Z edle!e d(d)d*Z"dmd+d,Z#ed-d. Z$ej%dfd/d0Z&ed1d2 Z'ed3d4 Z(d5d6 Z)ednd7d8Z*doed9d:d;Z+d<d= Z,ddddd'dd>d?d@Z-dddAdBdCZ.ddd'ddDdEdFZ/dGdH Z0dIdJ Z1dKdL Z2d'dMdNdOZ3dPdQ Z4ddRdSdTZ5 fdUdVZ6 fdWdXZ7dYdZ Z8d[d\ Z9d]d^ Z:d_d` Z;e fdadbZ<dcdd Z=dedf Z>dgdh Z?  Z@S )p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metadatac                    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)   ./data_providers/sqlalchemy.py
<dictcomp>D   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*   r-   ?   s    zSQLAlchemyDataDriver.__init__c                 C   sV   g }|j  D ]B\}}t|tr"q|jr:|d|ff q|jr|d|ff q|S )Nuniqueindex)r   r0   
issubclassr   r;   appendr<   )cls	cmf_modelZindexescmf_field_name	cmf_fieldr)   r)   r*   Z__get_indexesO   s    
z"SQLAlchemyDataDriver.__get_indexesc                 C   s&   | j |}|r"| j |= || j |< |S N)cached_queriesget)r?   keyretr)   r)   r*   	get_query]   s
    
zSQLAlchemyDataDriver.get_queryc                 C   s4   || j |< t| j dkr0| j D ]}| j |=  q0qd S )N   )rD   len)r?   queryrF   Ztmpr)   r)   r*   	set_queryg   s
    

zSQLAlchemyDataDriver.set_queryNc                 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}n~t|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! }nvt|tj"r~t }n^t|tj#rdS t|tj$rdS t|tj%rtj&}n$t|tj'rtj(}nt)d| tj|||j*|j|jdS )u0   Создадим SA поле для моделиN)primary_keynullableT)timezoneu"   Не найден тип поля )defaultrN   rM   )+
class_nameZvirtualr=   r   Z	CmfBigIntr.   ZBIGINTZCmfIntZIntegerZ
CmfNumericZNumericZ	precisionZscaleCmfTUUIDStringZ
max_lengthZforeign_keyr>   r   ZColumnrM   rN   ZCmfTextZTEXTZCmfStrCmfRelationCmfGenericRelationZCmfGenericM2MZCmfDateRangeZCmfM2MZCmfDateTime	TIMESTAMPZCmfDateZDATEZCmfJson
CmfBackrefCmfGenericBackrefZCmfBoolZBooleanZCmfTimeZTime	ExceptionrP   )r?   Z
_cmf_modelrB   Z_db_field_namecolumn_nameZsa_typer7   r)   r)   r*   _make_sa_columnr   sl    
  

z$SQLAlchemyDataDriver._make_sa_columnc                 C   s   t | jj||d}|S )N)schema)r	   r1   Zget_columns)r6   
table_namer\   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  )r[   typer3   executecommit)r6   r]   rZ   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М таблицу
        Tzcustom.r#   )abstract
__module____qualname__u   ID Объекта)captionrN   r<   left_idu   ID Элементаright_idu   Имя объекта)ri   rN   Zleft_name_cacheu   Имя элементаZright_name_cacheu-   Ошбика создания таблицы z: N)r   ZFieldrR   Z	CmfStr256ra   r   r.   r\   ZCreateTabledp_model	__table__r3   rb   rc   rY   transactionrollbackprint)r6   
model_namer8   r@   Zsmtprd   er)   r)   r*   add_custom_m2m_model   s    

z)SQLAlchemyDataDriver.add_custom_m2m_modelc                 C   s   |j }| j|}|s|j}g }g }i }|j D ].\}}	|}
| ||	|
}|dkrVq2|| q2| |D ]@\}}|dkr|t	j
d|ddi ql|t	j
d|  ql|| |t|d}|dd |D  t|| jf|}|| j|< |S )	u]   
        Вернём SA модель для CMF модели.
        TODO: db_name
        Nr;   T)Z__tablename____table_args__c                 S   s   i | ]}|j |qS r)   name)r&   cr)   r)   r*   r+      s      z5SQLAlchemyDataDriver.dp_model_cls.<locals>.<dictcomp>)N)N)__name__models_registryrE   	tablenamer   r0   r[   r>   "_SQLAlchemyDataDriver__get_indexesr.   ZIndextupleupdatera   sa_base_model)r?   r@   rq   sa_modelrz   Z
sa_columnsrt   Z__table_kwargs__rA   rB   Zsa_field_nameZ	sa_columnZ
index_typeZindex_tupleZsa_model_kwargsr)   r)   r*   dp_model_cls   s:    
z!SQLAlchemyDataDriver.dp_model_clsc                 C   s
   |  |S rC   )r   )r6   r@   r)   r)   r*   rl     s    zSQLAlchemyDataDriver.dp_modelc                 C   s   t t|jS )uG   
        Вернём CMF модель для SA модели.
        )getattrr   rx   )r6   r   r)   r)   r*   r@     s    zSQLAlchemyDataDriver.cmf_modelc                 C   s   t |tjS rC   )
isinstancer"   r~   )r6   Zdp_instancer)   r)   r*   is_instance  s    z SQLAlchemyDataDriver.is_instancec                 C   s0   i }|D ]}|j j||j jj< qtj|d|S )N
table_type)rl   rm   fullnamer.   r2   polymorphic_union)r6   Z
cmf_modelsaliasZtablesmodelr)   r)   r*   _union_models  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 EXISTSr   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rT|| S | dkrh||S | dkr~|| S || |S d S )Nr   print_debugr/   .u<   collect_filter_exp: Значение не загруженоZNULLr   r   r   r   )r   r   )r   r   r   r   r   r   r   r   r   )cmf.includer   r/   r   r   r   _valueDEBUGvaluelistr>   in_ZlikeZilikeop)operfieldvalr   r/   new_valr(   r)   r)   r*   _expression*  sR    












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   ru   )rQ   instance_classr   rE   _raise_invalid_fieldr=   rT   rU   CmfSubclassedGenericRelationr   related_modelsrJ   r.   r2   Zaliasedrl   rm   r   r   )r6   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_dataZ  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   r.   sql
selectableAliasrv   )r&   r   r)   r)   r*   	<setcomp>  s   z3SQLAlchemyDataDriver._make_joins.<locals>.<setcomp>_id)set	statementlocate_all_fromsr   Z	outerjoinr   r   r   rw   Zleft_field_nameid)rK   r   forceZavailable_joinsr   r)   r)   r*   _make_joinsz  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   )	rl   rm   rv   splitr   r>   r   r   r   )r6   	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   rQ   )r   r   Zfields_r)   r)   r*   r     s    z)SQLAlchemyDataDriver._raise_invalid_fieldc           #   	   C   s  |s|d fS t |d tr`g }|D ]0}| j|||||d\}}	|	d krHq"||	 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 rt|dks|d d	krtd
|||d }|d fS | 	|\}
}}t
t|tr |j}t
t|tr8|jj}t |ttfrg }|D ]N}t
t|trr||j n*t
t|tr||jj n
|| qP|}d|kr|d\}}}| j|||d}| ||}|d }|j}|j}d}n|d kr|jj}d}|j|}|s2td| d|j t
|tjrV| d}|j|}|d krl| || t
|tjrN|
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rP|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r4|n| fS td|
||nft
|tj0tj1fr|
dkr|
2dr|d nd!}
dd|g}|
d"krtd#|
||| j|d||d}|d krtd| d$| j3| d d d |d%}|j}|j}|j4 d}t5|j6|} |j#$| }!|d&|g}"t7|j6d'r8|"d'd&dgg}"|rF|"|g}"| j|"||j|!|d\}!}"|!%|"}!|!( }!|
2dr|! }!||!fS t
|tjrtd(| d)|
|||j8| j||||d}|d krtd| d*| d+|| )|
||fS d S ),Nr   )field_tableZANDr   OR)cmdr   order_by   )r   r   ut   Операция order_by в фильтре должна быть в формате: ["order_by", "=", [поля..]]r   r   )r   TFu	   Поле u    не найдено у r   )r   r   u   Правое значение для IN, NOT IN для m2m должно быть list, т.е. в квадратных скобках, например "field", "IN", "[obj.id]"u   Недопустимая операция над m2m полем (сравнение = только на None), допустимые IN, NOT IN, = None)r   r   r   r   r   
with_aliasu   В таблице u3    нет поля id для фильтрации m2mr   uD   Надо указать для поля либо left либо rightuh   Недопустимая операция над m2m полем, допустимые IN, NOT IN, = NonezNOT r   r   )r   r   ul   Недопустимая операция над backref полем, допустимые EXISTS, NOT EXISTSu7    нет поля id для фильтрации backref)r   r   cmf_deletedu   Фильтрация по u+    полю не поддерживаетсяu    нет поля u    для фильтрации)9r   r   collect_filter_expr>   r.   or_rJ   rY   order_queryr   r=   ra   r   r   r   r   r|   
rpartitionr   r   r   r   rl   rm   r   rE   r   rQ   CmfRelationBaser   
CmfM2MBase_query_columnZnested_fieldsr   ZRelationCachesessionrK   filterZ	parent_idZparent_fieldexistsr   Zchild_idm2m_model_clsrightrk   rj   leftrW   rX   r$   r   backrefr   rw   hasattr__mro__)#r6   Z	in_filterr   r   rK   r   r   Z
filter_resr(   Zexpr   r   r   Z_rightir   _r   r   r   r   Zfield_column	m2m_modelZm2m_subqueryZdp_m2m_modelZdp_m2m_field_selfZdp_m2m_field_theirZself_columnZbackref_dataZbackref_modelZbackref_tableZbackref_column_nameZbackref_columnZ	sub_queryZsub_filter_expr)   r)   r*   r     sJ   
     



"      









   


  

    


   
z'SQLAlchemyDataDriver.collect_filter_expc                 C   s
   |  dS )NZ_m2m_directr)   r   r)   r)   r*   _m2m_depth_alias_nameV  s    z*SQLAlchemyDataDriver._m2m_depth_alias_namec                 C   s
   |  dS )NZ_m2m_idr)   r   r)   r)   r*   _m2m_alias_nameZ  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   )	rE   r   r   r
   loadsr   r0   r   r>   )r6   r   r8   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   r.   r   r   r   rv   r   rw   Zlabelr   r%   )tablerZ   rK   r   Zfrom_columnr)   r)   r*   r   r  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   rE   r=   rT   rU   r   )r&   Zrelated_modelr   r   r)   r*   
<listcomp>  s
   z?SQLAlchemyDataDriver._select_joined_columns.<locals>.<listcomp>r   F)r   r   )r   rl   rm   r0   r   boolr>   Zright_modelsr   r   r   _select_joined_columnsr   )r6   Zfflr   r^   r   r   r   Zsub_fflr   r   Z
fields_clsZ
left_modelr   r   r)   r   r*   r   ~  s4        
z+SQLAlchemyDataDriver._select_joined_columnsc                O   s(  |  |}|  }ddlm} |d|j |}t| |}	|	rX|	 }	|		|}	nht

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

 |
 dkr|	 	d }t| || t|dkr|j}tt|D ]}|| ||| < q| ||}|r$| ||||	\}	}|	|}	|	S )Nr   	CMF_CACHEzquery: T)r   g{Gz?)rl   r3   r   r   hashrx   ra   rH   Z_cloneZwith_sessiontimer   rK   r   rL   rJ   rM   ranger   r   r   )r6   r   full_fields_loadr7   r8   r   rd   r   rF   rK   Zstart_calc_timer   r^   Zclone_qpkeyr   r   Z
filter_expr)   r)   r*   _create_query  s4    


z"SQLAlchemyDataDriver._create_query)r   r   slicegroup_by
for_updatemapperc                O   s   t |t }
t|}| |}| |	}	| j|fd|i|	 }|j|||d}|rb|j| }|rn|	 }|
 }|j|||d}|r| ||| |
r|  |S )   Грубо селектим всю таблицу, фильтры:
          - в kwargs: поле -> значение
        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)r6   r   r   r   r   r   r  r  r7   r8   mapper_is_my_argsqZsql_resZres_objsr)   r)   r*   r     s6    




  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)   )rm   rw   )r&   rZ   r   r)   r*   r     s     z.SQLAlchemyDataDriver.count.<locals>.<listcomp>Nr   count)r  r  r   r	  rl   r   Zselect_fromZfromsZwith_only_columnsr   r  r   r   r   r   rb   	enumerater>   Zscalar)r6   r   r   r   r7   r8   r  r  r^   Zcount_qres_listresrowZrow_resr   rZ   r  r)   r  r*   r    s>    


	
"zSQLAlchemyDataDriver.count)r   r   r  r  c                O   s   t |t }t|}| |}| |}| j|f|d|i|}	|	 }	|j|	|ddgd}	|rl|	 }	|		 }
|
r|j
|
||d}| j|g||d |r|  |S dS )u   
        Выбрать один объект по фильтру:
          - в args - первичный ключ(опционально)
          - в kwargs - другие поля: поле -> значениеr   r   r   r  r  )r  N)r   r   r  r  r  r   r	  r   r
  firstobject_to_cmfr  r  )r6   r   r   r   r  r  r7   r8   r  r  sa_instanceZres_objr)   r)   r*   rE     s     


zSQLAlchemyDataDriver.getc           :   
      s  |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 ]\}}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  | j||| |d
dt|gdd}|D ]T}|}|| D ]<}	 rt|	|}||_|jdkr||_nt|	|| qqqN|r| ||| | 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 ]D} |}!d| j	kr|dddgg}!| j| ||!d}"| |"|| j< q|sd	dlm }#m!}$ |#|$j"d|j d|j d q nVt#|dkrHt  t$t%|& ' }%n,t  | (t)j*+dd | D d' }%tt}&|%D ]}'t|'|}(|&|( |' qg })|D ]}	|&|	}* r$t|	|}|*sg |_|jdkrg |_q|,|*|| |_|jdkrt-|j|_|).|j n<|*s:t|	|g  q|,|*|| }t|	|| |).| q|)r| |)|| | q t|t	jr |j/rd}+d},nd}+d},|0 }-| 1|-}.t|.|+}/t|.|,}0| (|/|02|/3fdd|D }1tt}2tt}3t  |1D ]@\}4}5|5r|4s&q|3|5 |4 |2|5dd	  |5 q|2 D ]\}
}6tt|
}t  | j||| d
d|6g|dd})|)r| |)|| | |)D ]}	|3|	 D ]t}4||4 D ]d}7 r|7| }8|8jdkrg |8_|8j|	 n0t|7|d}|dkrg }t|7|| ||	 qƐqqqX|D ]f}	 r|	| }9|9jdkr`g |9_|9jdkrxt-|9j|9_|94  nt|	|ddkr<t|	|g  q<q qdS ) u   
        Прогрузка М2М и М2О полей
        :param objs: список обьектов
        :param full_fields_load: поля обьектов
        :return:
        Nc                    s    r| j jS | j S rC   )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 rC   )r   r   r)   r)   r)   r*   id_to_list_factory5  s    zWSQLAlchemyDataDriver._load_m2m.<locals>.model_to_id_factory.<locals>.id_to_list_factory)r   )r  r)   r)   r*   model_to_id_factory4  s    z;SQLAlchemyDataDriver._load_m2m.<locals>.model_to_id_factory.u#   Не загружено id поле:r   r   r   F)r   r  r   r   r   c                    s   g | ]} |qS r)   r)   r&   or  r)   r*   r     s     z2SQLAlchemyDataDriver._load_m2m.<locals>.<listcomp>r   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   rj   rk   c                    s   g | ]} |qS r)   r)   r   r"  r)   r*   r     s     )r   r   r  r   )5r   r   r   r   r>   rQ   r   r   r=   r   r   r   r   ZCmfBackrefBaser0   rE   r   Z_oldZid_fieldnamer   setattr	partitionr    r  r   dictr!   r   r   r   rz   r   r   r/   r   rJ   nextitervaluesr  rK   r.   r2   r   r  r   extendr   r   rl   r   r   Zapply_changes):r6   Zobjsr   r  r  Zto_load_mappingZmy_objs_by_idZmy_objs_by_model_nameZmodels_by_rel_fieldsobjrq   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	load_objsZload_objZ	loaded_idZmodels_listZobj_listZm2mqueryZbackref_field_nameZfilter_qZ	field_fflZ	rel_modelZmodel_query_filterr  r   r/   r  Zres_dictr   Zm2m_idZsub_objsZref_obj_listZmy_id_field_nameZsub_id_field_namer   r   Zmy_sa_fieldZsub_sa_fieldZ	m2m_queryZid_by_modelsZmy_id_by_sub_idZmy_idZsub_idZsub_id_listZmy_objZmy_fieldZ	obj_fieldr)   )r  r  r*   r    s   









   


 





   

zSQLAlchemyDataDriver._load_m2mc                 C   s6   g }|D ](}t |tjr&||j q|| q|S rC   )r   r   r   r>   r   )r6   r7   Znew_argsargr)   r)   r*   r  ,  s    zSQLAlchemyDataDriver.map_argsc                 C   s:   i }|  D ](\}}t|tjr,|j||< q|||< q|S rC   )r0   r   r   r   r   )r6   r8   Z
new_kwargsr'   r(   r)   r)   r*   r  5  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)r  r  r3   rl   r:   r   r  cmf_to_objectaddflushr   rn   ro   r   origr   lookupr   r   r   rY   is_newr  r  )r6   instancer-  r7   r8   r  _kwargsrd   r   r  r  rr   r+  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 rC   r   r&   fr6  r)   r*   	<genexpr>j  s     z.SQLAlchemyDataDriver.delete.<locals>.<genexpr>N)r  r  r3   rl   r:   ra   rM   r|   rK   rE   deleterY   rn   ro   )r6   r6  r7   r8   r  r7  rd   r   r   get_argsr  rr   r)   r<  r*   r>  b  s    



zSQLAlchemyDataDriver.delete)r   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 rC   r9  r:  r<  r)   r*   r=  y  s     z.SQLAlchemyDataDriver.update.<locals>.<genexpr>r.  )r/  r   )r  r  r3   rl   ra   rM   r|   rK   rE   r   r  r0  r2  r   rn   ro   r   r3  r   r4  r   r   r   rY   r5  r  r  )r6   r6  r   r7   r8   rd   r   r   r?  r  r  rr   r5  r+  r)   r<  r*   r}   s  s0    




zSQLAlchemyDataDriver.updatec                    s   t    d S rC   )r,   before_requestr6   r9   r)   r*   r@    s    z#SQLAlchemyDataDriver.before_requestc                    s   t    d S rC   )r,   before_first_requestrA  r9   r)   r*   rB    s    z)SQLAlchemyDataDriver.before_first_requestc                 C   s6   ddl m} |  |  |    | j  d S Nr   r   )r   r   cache_unlockZcache_transaction_startr3   rc   remover6   r   r)   r)   r*   rc     s
    zSQLAlchemyDataDriver.commitc                 C   s.   ddl m} |  |    | j  d S rC  )r   r   rD  r3   ro   rE  rF  r)   r)   r*   ro     s    zSQLAlchemyDataDriver.rollbackc                 O   s   |   j||S rC   r3   rK   r5   r)   r)   r*   rK     s    zSQLAlchemyDataDriver.queryc                 O   s   |   j||S rC   rG  r5   r)   r)   r*   query_deprecated  s    z%SQLAlchemyDataDriver.query_deprecatedc                    s   t  | d S rC   )r,   make_models)r?   r   r9   r)   r*   rI    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   rf   Zdata_sourcesrv   r   rl   )r6   rq   r@   r)   r)   r*   r4     s    
zSQLAlchemyDataDriver.init_metac                 C   sJ   | j | j tjt d}t|}|d| j	d  t
|d d S )Nzalembic.inizsqlalchemy.urlhead)db_metaZ
create_allr1   ospathjoingetcwdr   Zset_main_optionr/   r   Zstamp)r6   Zalembic_config_fileZalembic_configr)   r)   r*   init_db  s
    zSQLAlchemyDataDriver.init_dbc                 C   s   t   | j  d S rC   )r   r1   ZdisposerA  r)   r)   r*   r     s    z'SQLAlchemyDataDriver.close_all_sessions)N)N)NTN)F)N)NT)N)Arx   rg   rh   __doc__ry   r.   ZMetaDatarL  extZdeclarativeZdeclarative_baser~   rD   r-   classmethodr{   rH   rL   r[   r_   re   rs   r   rl   r@   r   r   staticmethodr   r   r   r   r   r   r   r   r   and_r   r   r   r   r   r   r   r   r  rE   r  r  r  r8  r>  r}   r@  rB  rc   ro   rK   rH  rI  r4   rQ  r   __classcell__r)   r)   r9   r*   r"      s    

	

F

.


-

 

 /

*   $  		$%	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   )r6   itemr)   r)   r*   __getattr__  s    zSAModelAccessor.__getattr__N)rx   rg   rh   rZ  r)   r)   r)   r*   rX    s   rX  )5rM  r   collectionsr   r   r   typingr   Zsqlalchemy.ext.declarativer.   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!   r"   rX  r)   r)   r)   r*   <module>   sB            D