U
    j*g^                    @   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dd Z fddZedd	 Zed
d Zedd ZedxddZdyddZd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 d&d' Z!e"d(d) Z#e"d*d+ Z$e%d,d-Z&dzd/d0Z'e"d{e(e& d2d3d4Z)d|d5d6Z*e"d7d8 Z+ej,dd1d1fd9d:Z-e"d;d< Z.e"d=d> Z/d?d@ Z0e"d}dAdBZ1d~e&dCdDdEZ2d1dd1dFdGdHZ3dddd1dd1d.ddd1dI
dJdKZ4dddLdMdNZ5ddd1dd1d1dOdPdQZ6ddRdSZ7dTdU Z8dVdW Z9d1dXdYdZZ:d[d\ Z;dd]d^Z<d1d1d_e=d`dadbZ>ddcdddeZ? fdfdgZ@dhdi ZAdjdk ZBdldm ZCdndo ZDe fdpdqZEdrds ZFdtdu ZGdvdw ZH  ZIS )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                 C   s   t jj| jdS )N)Zbind)
sqlalchemyormZsessionmakerengineself r+   "./cmf/data_providers/sqlalchemy.py_session_factoryC   s    z%SQLAlchemyDataDriver._session_factoryc                    sL   t  j|| tj| jfdd | D | _tj| 	 | _
|   d S )Nc                 S   s*   i | ]"\}}| d r|d dd|qS )zsqlalchemy. r   )
startswithreplace).0kvr+   r+   r,   
<dictcomp>g   s   
 z1SQLAlchemyDataDriver.__init__.<locals>.<dictcomp>)super__init__r&   Zengine_from_configconfigitemsr(   r'   Zscoped_sessionr-   Session	init_metar*   argskwargs	__class__r+   r,   r6   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_opsrC   )r   r8   
issubclassr   indexr@   Zindex_usingappendr&   ZIndex	tablename)cls	cmf_modelZindexescmf_field_name	cmf_fieldr<   r=   Zidx_typer+   r+   r,   Z__get_indexesp   sJ    

 
	
z"SQLAlchemyDataDriver.__get_indexesc                 C   s    | j |}|r| j | |S N)cached_queriesgetmove_to_end)rH   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 )rM   cached_queries_limitossysconflenpopitemprint)rH   queryrP   Zsys_memsize_mbZchunk_countZqueries_limitZevicted_keyrB   r+   r+   r,   	set_query   s    
zSQLAlchemyDataDriver.set_queryc                 C   s4  |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rt|j}n$t|tjrdS t|tjrdS t|tjr,dS t|tjr>dS t|tjrPdS t|tj rltj!dd}nt|tj"rt# }nt|tj$rt }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+rddl,m-} |}nt.d| tj|||j/|j|jdS )	u0   Создадим SA поле для моделиN)primary_keynullableT)timezoner   )TSVECTORu"   Не найден тип поля )rA   r`   r_   )0
class_nameZvirtualrD   r   Z	CmfBigIntr&   ZBIGINTCmfIntZIntegerZ
CmfNumericZNumericZ	precisionZscaleCmfTUUIDStringZ
max_lengthZforeign_keyrF   r   ZColumnr_   r`   ZCmfTextZTEXTZCmfStrZCmfBytesZLargeBinaryCmfRelationCmfGenericRelationZCmfGenericM2MZCmfDateRangeZCmfM2MCmfDateTime	TIMESTAMPZCmfDateZDATEZCmfJson
CmfBackrefCmfGenericBackrefCmfBoolZBooleanZCmfTimeZTimeZCmfTsVectorZsqlalchemy.dialects.postgresqlrb   	ExceptionrA   )rH   Z
_cmf_modelrK   Z_db_field_namecolumn_nameZsa_typer<   rb   r+   r+   r,   _make_sa_column   sv    
  

z$SQLAlchemyDataDriver._make_sa_columnc                 C   s   t | jj||d}|S )N)schema)r
   r(   Zget_columns)r*   
table_namerq   columnsr+   r+   r,   inspect_table_columns  s    z*SQLAlchemyDataDriver.inspect_table_columnsc                 C   s   |  |}| jj| j|jS rL   )dp_modelr(   Zdialect	has_table	__table__)r*   rI   sa_modelr+   r+   r,   rv   	  s    
zSQLAlchemyDataDriver.has_tablec                 C   s   |  d |j}t|tjr$| d}|  }d| d| d| }|jsn|jd kr\d| | d|j d}|| |	  d S )NZTZzalter table z add column  uH   Значение по умолчанию не может быть None: z	 default z	 NOT NULL)
rp   typerD   r   ri   r9   r`   rA   executecommit)r*   rr   ro   Zcmf_field_typeZsa_column_typessqlr+   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`   rE   left_idu   ID Элементаright_id   Имя объекта)r   r`   Zleft_name_cacheu   Имя элементаZright_name_cache-   Ошбика создания таблицы : N)r   Fieldre   	CmfStr256rz   r   r&   rq   CreateTableru   rw   r9   r{   r|   rn   transactionrollbackr\   )r*   
model_namer=   rI   smtpr}   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 )NTr   r.   r   u)   Идентификатор объектаu3   Автоматически генерируетсяF)r   commentr`   r_   readonlyvisibleidr   )r   rE   nameu   Сортировкаr   )r   rE   r   r`   rA   Zordernou   Кодu3   Код в реальном мире из жизни)r   r@   r   r   r`   codeu:   Код родителя в каскадном выборе)r   r   r`   rE   Zchoice_parent_idu   СкрытьZ
cmf_hidden)	r   r   re   r   rd   ZCmfStr64rm   rz   r   )r*   r   r=   rI   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   r&   rq   r   ru   rw   r9   r{   r|   rn   r   r   r\   )r*   r   rI   r   r}   r   r+   r+   r,   add_custom_choice_modela  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   )r1   cr+   r+   r,   r4     s      z5SQLAlchemyDataDriver.dp_model_cls.<locals>.<dictcomp>)__name__models_registryrN   rG   r   r8   rp   rF   extend"_SQLAlchemyDataDriver__get_indexestupleupdaterz   sa_base_model)rH   rI   r   rx   rG   Z
sa_columnsr   Z__table_kwargs__rJ   rK   Zsa_field_nameZ	sa_columnZsa_model_kwargsr+   r+   r,   dp_model_clsp  s0    
z!SQLAlchemyDataDriver.dp_model_clsc                 C   s
   |  |S rL   )r   )r*   rI   r+   r+   r,   ru     s    zSQLAlchemyDataDriver.dp_modelc                 C   s   t t|jS )uG   
        Вернём CMF модель для SA модели.
        )getattrr   r   )r*   rx   r+   r+   r,   rI     s    zSQLAlchemyDataDriver.cmf_modelc                 C   s   t |tjS rL   )
isinstancer%   r   )r*   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)ru   rw   fullnamer&   r'   polymorphic_union)r*   Z
cmf_modelsaliasZtablesmodelr+   r+   r,   _union_models  s    z"SQLAlchemyDataDriver._union_modelsc	                    sB   d fdd	}	i }
|D ]}|j j|
|j jj< q|	|
d|S )Np_unionTc           
   
      s  t j }i i | D ]t}| | tt jjr@ | |< i }jD ]6}r^|jkr^qJ|	|j |||j< |j
|j< qJ|< q fddg }|  D ]\}	|d k	r$|t jjfdd|D t jt jj|	|g gdtj djjk q|t jjfdd|D gdtj djjk qt jj| |S )Nc                    sp   z| |  W S  t k
rj    rDtjtj |  |  Y S tjtj |  |  Y S Y nX d S rL   )KeyErrorr&   r~   castZnulllabelZtype_coerce)r   table)
cast_nullscolnamemapstypesr+   r,   col  s    "zPSQLAlchemyDataDriver._union_models2.<locals>.hack_polymorphic_union.<locals>.colc                    s   g | ]} |qS r+   r+   r1   r   r   r   r+   r,   
<listcomp>  s     zWSQLAlchemyDataDriver._union_models2.<locals>.hack_polymorphic_union.<locals>.<listcomp>)Zfrom_obj_idc                    s   g | ]} |qS r+   r+   r   r   r+   r,   r     s     )r&   utilZ
OrderedSetr   r~   ZSelectr   r   rP   addrz   r8   rF   ZselectZliteral_columnZ_quote_ddl_exprr   wherer   r   Zcorrelate_exceptZ	union_all)
Z	table_mapZtypecolnameZ	aliasnamer   ZcolnamesrP   mr   resultZtype_left_field_name
left_tablesub_ffl)r   r   r   r   r   r,   hack_polymorphic_union  s`    



	
	 zCSQLAlchemyDataDriver._union_models2.<locals>.hack_polymorphic_unionr   )r   T)ru   rw   r   Zlateral)r*   r   right_tabler   r   right_modelmodels_is_unionr   r   Z	join_subqr   r+   r   r,   _union_models2  s       =z#SQLAlchemyDataDriver._union_models2c                 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_debugr7   .u<   collect_filter_exp: Значение не загруженоZNULLr   r   r   r   )r   r   )r   r   r   r   r   r   r   r   r   r   r   )cmf.includer   r7   r   r   r   _valueDEBUGvaluelistrF   in_ZlikeZilikeop)operfieldvalr   r7   new_valr3   r+   r+   r,   _expression   sZ    
















z SQLAlchemyDataDriver._expressionJoinDatazJleft_table, right_table, alias, left_field_name, right_model, right_modelsTc              
   C   s0  |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}n2|d k	r| j|d |||
|	d|d}n| |	|}| |||||
|	}|S )NuX   Недопустимый тип поля для вложенной фильтрации .up   . Вложенная фильтрация возможно только для CmfRelation и CmfGenericRelationZ_sub__uj   Не возможно построить запрос для вложенной фильтрации по u   , т.к. у поля u#    не указаны related_modelsr   r   r   T)r   r   )rc   instance_classr   rN   _raise_invalid_fieldrD   rg   rh   CmfSubclassedGenericRelationr   related_modelsrZ   r&   r'   Zaliasedru   rw   r   r   r   )r*   r   r   
field_name
from_aliasis_models_required	field_clsr   Z
alias_partr   r   r   	join_datar+   r+   r,   _calc_join_data4  s:    

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&   r~   
selectableAliasr   )r1   r   r+   r+   r,   	<setcomp>a  s   z3SQLAlchemyDataDriver._make_joins.<locals>.<setcomp>r   )set	statementlocate_all_fromsr   Z	outerjoinr   r   r   r   r   r   )r]   r   forceZavailable_joinsr   r+   r+   r,   _make_joinsW  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   )	ru   rw   r   splitr   rF   r   r   r   )r*   	join_pathr   r   r   r   r   r   r+   r+   r,   _calc_joins_by_patho  s    

z(SQLAlchemyDataDriver._calc_joins_by_pathc                 C   s0   t |jdd}t|  d|j d| d S )N,z,
u+    не существует у модели u   , но есть: 
)strr   r0   r   rc   )r   r   Zfields_r+   r+   r,   r     s    z)SQLAlchemyDataDriver._raise_invalid_fieldc	           0   
   C   sp	  |s|d fS t |d trdg }	|D ]4}
| j|
||||||d\}}|d krLq"|	| 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rD|jj}t |ttfrg }|D ]r}t
t|trt
t|jtr||jjj n||j n*t
t|tr||jj n
|| q\|}|d}|j|d }d}t|dkrPt
|tjtjtjfrP|d }d|dd  ||g}d}|d kr|jj}nrt|dkr|d\}}}| j|||d}| ||}|d }|j}|j|}|j}d}n|d kr|jj}|dkr|dkr|jd}|std| d|j  |d kr| !|| t
|tjr|dkrRt |tsRt |tsRtd||||d	krt|d k	rttd||||dkr| j"|d||d}|d krtd| d|j#r.| t$j%}|d	kr|j&'|(|j)|k|j*|j k+ }|| fS |j&'|(|j)|k|j*|j k| ,d|j-|+ }n|. }| |}|j/rV|j0}|j1}n"|j2rl|j1}|j0}ntd|||d	kr|j&'|(||k+ }|| fS |j&'|(||k| ,d||+ }||dkr|n| fS |d kr| j"|d||d}|d krtd| d!| j3| d d d |d"}|j} |j}!|j&'|!j4j}"|j#r| t$j%}#|#j}$|"|$|#j-|!j4jk}"|"(|#j)|k|#j*|j k}"nh|j2rd#}%d$}&nd$}%d#}&|. }| |}#|#j}$t5|#|%}'t5|#|&}(|"|$|(|!j4jk}"|"(|'|k}"t6|!j4d%r|sd%d&dg})ng })t6|!j4d'rD|sD|)d'd&dgg})|rR|)|g})| j|)| | j|"|!||d\}"})|"(|)}"|"+ }"|7d(r|" }"||"fS td)|||n$t
|tjtjfr|dkr|7d(rd*nd}dd|g}|d krtd+|||| j"|d||d}|d kr.td| d,| j3| d d d |d"}*|*j}+|*j},|j8 d-}-t5|,j4|-}.|j&'|.}"|-d&|g})t6|,j4d%r|)d%d&dgg})|r|)|g})| j|)|+|+j|"|,||d\}"})|"(|)}"|"+ }"|7d(r|" }"||"fS t
|tj9r| d-}|d.kr| j3| d d d |d"}|j} |j}!|j&'|!j4j}"| t$j%}#|#j}$|"|$|#j-|!j4jk}"|"(t| ,d|#j)|| ,d|#j:||#j*d/k}"| j"||||d}|t| ,d||| ,d||"fS |dk	r(t$j;j<|j ||ddd0gd1}/|/	sd2g}/| j"|d||d}|| ,d||/fS | j"||||d}|d k	rZtd| d3| d4|| ,|||fS d S )5Nr   )field_tableinclude_deletedinclude_dummyZANDr   OR)cmdr
  r  r  order_by   )r   r   ut   Операция order_by в фильтре должна быть в формате: ["order_by", "=", [поля..]]r   r   Fr   )r   T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   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_exprF   r&   or_rZ   rn   order_queryr   rD   rz   r   r   r   r   r   r  r   rN   
CmfM2MBaserk   rl   joinru   rw   
rpartitionr  r  r   r   r   rc   r   _query_columnZnested_fieldsr   ZRelationCachesessionr]   filterZ	parent_idZparent_fieldexistsr   Zchild_idm2m_model_clsrightr   r   leftr   r   r   hasattrr/   backrefCmfRelationBaseZparent_codeZCmfFullSearchZfulltext_search)0r*   Z	in_filterr   rx   r]   r  r
  r  r  Z
filter_resr3   Zexpr   r$  r#  Z_rightiZ
left_partsr   r  r  rB   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   )	rN   r   r	  r   loadsr   r8   r   rF   )r*   r   r=   smart_filterr2   r3   r+   r+   r,   _get_filter  s    


z SQLAlchemyDataDriver._get_filterc                 C   s   |r4|j  D ]$}t|tjjjr| j|jkr|} qd|kr|d\}}|dkrvt	| j
|d}t	t|||}qt	| j
|d}nt	| j
|d}|dk	r|r|t|ddS |S dS )u   Для полей "основной" таблицы не нужен префикс таблицы, для этого нужно указать with_alias=Falser   )sumZavgcountminmaxNr   )r  r  r   r&   r~   r   r   r   r  r   r   r	   r   r	  r0   )r   ro   r]   r  Zfrom_Z	func_nameZcolumn_name_shortcolumnr+   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   rN   rD   rg   rh   r   )r1   Zrelated_modelr   r   r+   r,   r     s
   z?SQLAlchemyDataDriver._select_joined_columns.<locals>.<listcomp>r   F)r   r   r9  )r   ru   rw   r8   r  boolrF   Zright_modelsr   r   r   _select_joined_columnsr   )r*   Zfflr   rs   r   r:  r   r   r8  r   Z
fields_clsZ
left_modelr   r   r+   r;  r,   r=    s6         
z+SQLAlchemyDataDriver._select_joined_columns)r  group_byr  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\}}||}|rg }|D ]T}||jjkr`||jj|  n,| d	|jjkr:||jj| d	  q:|j| }|S )
Nr   	CMF_CACHEzquery: full_fields_loadT)r  g{Gz?r  r  r   )ru   r9   r   r@  hashr   rz   rR   Z_cloneZwith_sessiontimer=  r]   r  r^   rZ   r_   ranger3  r  r   rw   r   rF   r>  )r*   r   rB  r  r>  r  r<   r=   rx   r}   r@  rP   r]   Zstart_calc_timer   rs   Zclone_qpkeyr(  r2  
filter_expro   r+   r+   r,   _create_query  sH    




z"SQLAlchemyDataDriver._create_query)
r  rB  r  
for_updatemapperr  load_m2mr>  aggregate_selectr  c       
         O   s   t |t }t|}| |}| |}| j|f||||	|
d| }|j||||
d}|rl|j| }|
r|	 }td}|j
|||d}|S |r| }|	 }|j
|||d}|r|r| j|||||d |r|  |S dS )   Грубо селектим всю таблицу, фильтры:
          - в kwargs: поле -> значение
        )rB  r  r  r>  rM  )r  rM  ZsimplerB  r   rC  N)r   r   
get_mappermap_args
map_kwargsrI  with_labelsr  r  alllist_to_cmfwith_for_update	_load_m2mpost_mapping_hook)r*   r   r  rB  r  rJ  rK  r  rL  r>  rM  r  r<   r=   mapper_is_my_argsqZsql_resZres_objsr+   r+   r,   r   &  sJ    


    


  zSQLAlchemyDataDriver.list)rB  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 )rN  rB  c                    s   g | ]} j j| qS r+   )rw   r   )r1   ro   rx   r+   r,   r   Y  s     z.SQLAlchemyDataDriver.count.<locals>.<listcomp>Nr  r5  )rQ  rR  rI  rS  ru   r  select_fromZfromsZwith_only_columnsr	   r5  r>  r  r   r  r{   	enumeraterF   Zscalar)r*   r   rB  r>  r<   r=   rZ  r[  rs   Zcount_qres_listresrowZrow_resr(  ro   r5  r+   r\  r,   r5  H  s>    


	
"zSQLAlchemyDataDriver.count)r  rB  rJ  rK  r  r  c                O   s   t |t }
t|}| |}| |	}	| j|f||||d|	}| }|j||ddgd}|rp| }|	 }|r|j
|||d}| j|g||||d |
r|  |S dS )u   
        Выбрать один объект по фильтру:
          - в args - первичный ключ(опционально)
          - в kwargs - другие поля: поле -> значение)rB  r  r  r   r   )r  rO  )rK  r  r  N)r   r   rP  rQ  rR  rI  rS  r  rV  firstobject_to_cmfrW  rX  )r*   r   r  rB  rJ  rK  r  r  r<   r=   rY  r[  sa_instanceZres_objr+   r+   r,   rN   l  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  d
dt|g}| j||| ||d}|D ]H}|}|| D ]0} rt||}||_||_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 ]n}#|!}$d|#j	kr|s|!dddgg}$d|#j	kr|s|$dddgg}$| j|#|"|$||d}%|#|%||#j< q|sDd	dlm }&m!}' |&|'j"d|j d|j d qnVt#|dkrnt  t$t%|& ' }(n,t  | (t)j*+dd | D d' }(tt})|(D ]}*t|*| }+|)|+ |* qg },|D ]}|)|}- rJt||}|-sg |_|jdkrg |_q|,|-|| |_|jdkr<t-|j|_|,.|j n<|-s`t||g  q|,|-|| }t||| |,.| q|,r| j|,|| |||d qt|t	jr|j/rd}.d}/nd}.d}/|0 }0| 1|0}1t|1|.}2t|1|/}3| (|2|32|23fd d|D }4tt}5tt}6t  |4D ]@\}7}8|8r:|7sRq:|6|8 |7 |5|8dd	  |8 q:|5 D ](\}}9tt|}t  d
d|9g}|st4|drdddg|g}|st4|drdddg|g}| j||| ||dd!},|,r| j|,|| |||d |,D ]}|6| D ]t}7||7 D ]d}: rp|:| };|;jdkrbg |;_|;j| n0t|:|d}|dkrg }t|:|| || q>q2q"q|D ]f} r|| }<|<jdkrg |<_|<jdkrt-|<j|<_|<5  nt||ddkrt||g  qqqdS )"u   
        Прогрузка М2М и М2О полей
        :param objs: список обьектов
        :param full_fields_load: поля обьектов
        :return:
        Nc                    s    r| j jS | j S rL   )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 rL   )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   )rg  r+   r+   r,   model_to_id_factory  s    z;SQLAlchemyDataDriver._load_m2m.<locals>.model_to_id_factory.u#   Не загружено id поле:r   r   r   )rB  rK  r   rC  r   c                    s   g | ]} |qS r+   r+   r1   orf  r+   r,   r     s     z2SQLAlchemyDataDriver._load_m2m.<locals>.<listcomp>r  r   Fr  )rB  r   r  r  r   zORM: skip load field r   z die no related modelsr   c                 S   s   i | ]\}}||j qS r+   )r  )r1   r2   r   r+   r+   r,   r4   :  s      z2SQLAlchemyDataDriver._load_m2m.<locals>.<dictcomp>r   r   r   c                    s   g | ]} |qS r+   r+   rj  rl  r+   r,   r   p  s     )rB  r   rK  rL  )6r   r    r   r   rF   rc   r   r   rD   r   r   r'  r  ZCmfBackrefBaser8   rN   r   Z_oldZid_fieldnamer   setattr	partitionr!   rW  r&  dictr"   r   rI  r  rG   r   r   r7   r   rZ   nextitervaluesrT  r]   r&   r'   r   rU  r   r   r$  r"  ru   r   r   r%  Zapply_changes)=r*   ZobjsrB  rK  r  r  rh  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   r7   r_  Zres_dictr   Zm2m_idZsub_objsZref_obj_listr*  r+  r)  rx   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+   )re  rf  r,   rW    s   









  


 







   

zSQLAlchemyDataDriver._load_m2mc                 C   s6   g }|D ](}t |tjr&||j q|| q|S rL   )r   r   r   rF   r   )r*   r<   Znew_argsargr+   r+   r,   rQ    s    zSQLAlchemyDataDriver.map_argsc                 C   s:   i }|  D ](\}}t|tjr,|j||< q|||< q|S rL   )r8   r   r   r   r   )r*   r=   Z
new_kwargsr2   r3   r+   r+   r,   rR    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)rQ  rR  r9   ru   r?   r   rP  cmf_to_objectr   flushr   r   r   r   origr   lookupr   r   r   rn   is_newrc  rX  )r*   instanceru  r<   r=   rZ  _kwargsr}   rx   rd  rK  r   rs  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 rL   r   r1   fr}  r+   r,   	<genexpr>  s     z.SQLAlchemyDataDriver.delete.<locals>.<genexpr>N)rQ  rR  r9   ru   r?   rz   r_   r   r]   rN   deletern   r   r   )r*   r}  r<   r=   rZ  r~  r}   rx   rG  get_argsrd  r   r+   r  r,   r    s    



zSQLAlchemyDataDriver.deletec                 K   s`   |  |}|  }||}| ||}|rP| j||||||d\}}	||	}|jdd}
|
S )NrC  Zfetch)Zsynchronize_session)ru   r9   r]   r3  r  r   r  )r*   r   r  r  r=   rx   r}   r]   r2  rH  Z
delete_cntr+   r+   r,   bulk_delete  s     

     

z SQLAlchemyDataDriver.bulk_deleterC  )rr  c                O   s   ddl m} | |}|  }	|	|}
| ||}|r\| j||||
||d\}
}|
|}
|
j}|
j	}||
||j}|D ]}||}q||}|	|}dd |D S )Nr   )r   rC  c                 S   s   g | ]}|d  qS )r   r+   )r1   ra  r+   r+   r,   r     s     z4SQLAlchemyDataDriver.bulk_update.<locals>.<listcomp>)r&   r   ru   r9   r]   r3  r  r   Z
_criterionZ_join_entitiesrr  Z	returningr   r]  r   r{   )r*   r   rr  r  r  r<   r=   r   rx   r}   r]   r2  rH  Zfilter_criteriaZjoin_clausesZstmtZjoin_clauser   r+   r+   r,   bulk_update  s$    





z SQLAlchemyDataDriver.bulk_updaterA  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 rL   r  r  r  r+   r,   r    s     z.SQLAlchemyDataDriver.update.<locals>.<genexpr>rv  )rw  rB  )rQ  rR  r9   ru   rz   r_   r   r]   rN   r   rP  rx  ry  r   r   r   r   rz  r   r{  r   r   r   rn   r|  rc  rX  )r*   r}  rB  r<   r=   r}   rx   rG  r  rd  rK  r   r|  rs  r+   r  r,   r     s0    




zSQLAlchemyDataDriver.updatec                    s   t    d S rL   )r5   before_requestr)   r>   r+   r,   r  9  s    z#SQLAlchemyDataDriver.before_requestc                 C   sB   ddl m} |  |  |    | j  t  t  d S Nr   r?  )	r   r@  cache_unlockZcache_transaction_startr9   r|   remover#   r$   r*   r@  r+   r+   r,   r|   <  s    
zSQLAlchemyDataDriver.commitc                 C   s:   ddl m} t  |  |    | j  t  d S r  )r   r@  r#   r  r9   r   r  r$   r  r+   r+   r,   r   G  s    
zSQLAlchemyDataDriver.rollbackc                 O   s   |   j||S rL   r9   r]   r;   r+   r+   r,   r]   O  s    zSQLAlchemyDataDriver.queryc                 O   s   |   j||S rL   r  r;   r+   r+   r,   query_deprecatedR  s    z%SQLAlchemyDataDriver.query_deprecatedc                    s   t  | d S rL   )r5   make_models)rH   r   r>   r+   r,   r  U  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   r   Zdata_sourcesr   r   ru   )r*   r   rI   r+   r+   r,   r:   Z  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)r9   r{   r|   db_metaZ
create_allr(   rX   pathr  getcwdr   Zset_main_optionr7   r   Zstamp)r*   r}   Zalembic_config_fileZalembic_configr+   r+   r,   init_dbl  s    
zSQLAlchemyDataDriver.init_dbc                 C   s   t   | j  d S rL   )r   r(   Zdisposer)   r+   r+   r,   r   x  s    z'SQLAlchemyDataDriver.close_all_sessions)N)N)NTNN)F)N)NT)N)FF)FF)Jr   r   r   __doc__r   r&   ZMetaDatar  extZdeclarativeZdeclarative_baser   r   rM   rW   r-   r6   classmethodr   rR   r^   rp   rt   rv   r   r   r   r   r   ru   rI   r   r   r   staticmethodr   r   r   r   r   r   r  r  r   and_r  r/  r0  r3  r  r=  rI  r   r5  rN   rW  rQ  rR  r  r  r  ro  r  r   r  r|   r   r]   r  r  r:   r  r   __classcell__r+   r+   r>   r,   r%      s    
)

K
3
&C

1

#


  "

3      "$
  $		$
%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   )r*   itemr+   r+   r,   __getattr__~  s    zSAModelAccessor.__getattr__N)r   r   r   r  r+   r+   r+   r,   r  }  s   r  )9rX   rE  collectionsr   r   r   typingr   r   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"   Zutil.cmfutilr#   r$   r%   r  r+   r+   r+   r,   <module>   sL               k