
    *j                    B   d Z ddlZddlZddlZddlZddlZddlZddlmZ ddl	m
Z
 ddlmZ ddl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mZmZmZmZ ddl Z!ddl"Z!ddl  ddl# ddl$m%Z% ddl&m'Z'm(Z(m)Z)m*Z*m+Z+m,Z, e G d d             Z- G d d      Z.y)u  Custom-field генерация и runtime-синхронизация для CmfCustomClass.

Модуль вынесен из base_model.py, чтобы базовая модель не содержала тяжёлую
логику custom-field import/reload/delta. base_model.py оставляет re-export
CmfCustomField и CmfCustomClass для совместимости старых импортов.
    N)defaultdict)Mapping)	timedelta)RLock)deepcopy)	dataclass)ProgrammingError)translit)DictListOptionalSetTupleType)*)cmfutil)	BaseModel	CmfEntityCmfGM2MModelCmfM2MModelDEFAULT_DATASOURCEcamel2snakec                   $   e Zd ZU dZeed<   dZeed<   dZeed<   dZe	e   ed<   dZ
eed<   dZeed<   dZeed<   dZeed	<   dZeed
<   dZee   ed<   dZee   ed<   dZeed<   dZeed<   dZe	e   ed<   dZeed<   dZeed<   dZe	e   ed<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   dZeed<   dZ eed<   dZ!eed<   dZ"eed<   dZ#eed<   dZ$e	e   ed<   dZ%e	e   ed <   dZ&e	e   ed!<   d"Z'e(ed#<   dZ)eed$<   e*d)d%efd&       Z+d)d'Z,d( Z-y)*CmfCustomFieldN
class_name
field_typeui_group_name	ui_groupsui_metacaptioncustomfullsearch_indexdb_typedb_column_namedb_table_namewidgetmodelmodelsbackrefchoicesdisabled_choicesFdb_need_create_choice_tabledb_choice_table_namedefaultrightleftplaceholdercommentTvisiblereadonlyrequiredoptions_list_paramsoptions_list_show_columnsoptions_list_search_columnsr   custom_table_nocf_field_version
field_dictc                 X   t        |      }i }d }i }|j                         D ]a  \  }}|dk(  r3t        |t              r#|D ci c]  }t        j                  |      | }}|| j                  v r|||<   |dvr|||<   |dk(  s`|}c  | dd|xs |i|}	|	j                  |       |	S c c}w )Nr*   )r   r   r   r   r#   r$   r%   required_fieldsr    )r   items
isinstancelistCmfCustomClasscaption_to_name__dataclass_fields___calc_db_choice_table_name)
clsr;   r'   r   ui_meta_nativekwargskvchoiceress
              ./cmf/models/cmf_custom_class.py	from_dictzCmfCustomField.from_dictL   s    j)
$$& 
	#DAqI~*Q"5RST^33F;VCTTC,,,q	 U U 
I~!"
	# >.3G>v>&&u-
 Us   B'c                 Z   |d}dj                  | j                  j                  d      D cg c]  }|j                          c}      }|j                   | | }t	        |      dkD  r;t
        j                  j                  ddt	        |j                        z
   dd	       |S c c}w )
NCh _@   u8   Имя поля не должно быть больше    u    символовTabort)joinr   split
capitalizelencmfinclude	cmf_alert)selfr'   suffixpr-   s        rM   gen_db_choice_table_namez'CmfCustomField.gen_db_choice_table_namea   s    >F!ww@U@UVY@Z'[1'[\"'"2"2!34H3I&R#$r)KK!!$\]dehiniyiyez]z\{  |M  #N  VZ!  [## (\s   B(c                    t        | dd       }|sy |dk(  s|dk(  rRd| _        |dk(  rdnd }| j                  ||      | _        | j                  | _        | j                  | j
                  d<   y || j                  |      k(  r|| _        y y )Nr'   CUSTOM_CHOICE_MODELCUSTOM_CHOICE_MODEL_EXTTExt)getattrr,   ra   r-   r'   r   )r^   r'   	rel_modelr_   s       rM   rE   z)CmfCustomField._calc_db_choice_table_namek   s    D'40	-->W1W/3D,'+DDU$F(,(E(EeV(TD%22DJ$(JJDLL!$77>> )2D% ?    N).__name__
__module____qualname__r   str__annotations__r   r   r   r   r   dictr    r!   boolr"   r#   r$   r   r%   r&   r'   r(   r)   r*   r+   r,   r-   r.   r/   r0   r1   r2   r3   r4   r5   r6   r7   r8   r9   intr:   classmethodrN   ra   rE   r>   rh   rM   r   r   )   s   JJM3ItCyGTGSFD!d!GS$(NHSM(#'M8C='FCE3FDIGSGT"&d3i&(-- $#$GSE4D$KGSGTHdHd%)c)+/tCy/-1c1OS c 4  ($2rh   r   c                   *   e Zd ZdZ ed      Z e       Zi Ze	d        Z
e	d        ZdZdZ edd	d
ddddddddddddhe      Zed        ZdZedefd       Ze	d        Zedefd       Zdeeef   fdZ	 ddeeeef   eeef   f   fd Zed!edefd"       Zdeeef   fd#Z e	d$edefd%       Z!e	d&edefd'       Z"e	d$ed(efd)       Z#e	d$efd*       Z$e	d!ed&edefd+       Z%e	d&efd,       Z&e	d&edefd-       Z'ede(fd.       Z)ed$ededefd/       Z*d$ede(fd0Z+e	d!ed&ede(fd1       Z,d!ed$ed&edefd2Z-dd!ed3ed4edefd5Z.d!ed3edefd6Z/ed$ededefd7       Z0ed!ed&edefd8       Z1d$ededefd9Z2edded$ede3e   fd:       Z4	 	 dd$ed&ed;ed<edeeef   f
d=Z5d&eddfd>Z6dddd?d$ed<ed@edAedef
dBZ7d$edCefdDZ8d$edeeef   fdEZ9de3fdFZ:dG Z;dd3efdHZ<d3ed&edee3e   e3e   f   fdIZ=d3ed&edeeef   fdJZ>d3ed&efdKZ?ddLZ@d3efdMZAd3efdNZBd3edefdOZCedefdP       ZDedefdQ       ZEedRedefdS       ZFd3eddfdTZGddUZHd&eddfdVZId3edefdWZJedefdX       ZKedRedefdY       ZLd3eddfdZZMed[eNe   ddfd\       ZOedd]       ZPd^efd_ZQd`eeef   daeNe   defdbZRdceddfddZSdeeddfdfZTdgedheeef   deNe   fdiZUdje3e   dkeeef   defdlZVddeeef   fdmZWi ZXe	dn        ZYe	ddo       ZZe e[dpq      drefds              Z\e	ddt       Z]e e[duq      dv               Z^dwe_e`   fdxZadye3e   fdzZbd{ Zcd| Zdd} Zed~ ZfddZgededefd       Zhedefd       Zied&efd       Zjd Zkd Zle	d        Zmy)rB   u6   Вся логика кастом полей здесь   )minutesc                     | j                   5  | j                  j                  |t                     cddd       S # 1 sw Y   yxY w)u{   Вернуть локальную gevent-блокировку для атомарной правки меты модели.N)_cf_delta_locks_guard_cf_delta_locks
setdefaultr   )rF   r   s     rM   _cf_delta_lock_for_modelz'CmfCustomClass._cf_delta_lock_for_model   s>     && 	G&&11*egF	G 	G 	Gs	   $;Ac                 &    |y|| j                   z
  S )u   Вернуть нижнюю границу DB-фильтра с нахлёстом для пограничных изменений.N)_CF_DELTA_CHANGED_SINCE_OVERLAP)rF   changed_sinces     rM   _cf_delta_filter_changed_sincez-CmfCustomClass._cf_delta_filter_changed_since   s      sBBBBrh   )r   r    r9   r'   r(   r*   r+   r.   r3   r4   r5   r&   r1   r2   r"   r6   r7   r8   r/   r0   r)   r-   r,   )r'   r(   r*   r+   r.   r3   r4   r5   r&   r1   r2   r"   r6   r7   r8   r/   r0   r)   r9   r-   r   r   r   r   r#   r$   r%   r=   internal_typeui_meta_skipinstance_classr!   class_full_nameui_form_visibler:   c                     t        | d|       S )uu   Достать сырое значение из Eva field wrapper или вернуть объект как есть.value)rf   )	raw_values    rM   _valuezCmfCustomClass._value   s     y'955rh   )namer    r   r/   r0   r1   r2   r.   r3   field_readonlyr"   r5   r(   r'   r*   r+   r6   r7   r8   r&   field_custom_typeallow_multichoicefilter_cur_projectis_ext_datasourcer9   cmf_modified_atcmf_deletedreturnc                     ddl m} t        | d      st        j	                  |       } | j                         }t        | dd      }||S  ||      }|t        j                  dd	       |S ||d
<   |S )u}   Собрать метаданные генерации и времени выполнения из строки CmfCustField.r   )format_cf_field_versionget_meta)simple_objectr   Nz?cf-field-version: cannot normalize CmfCustField.cmf_modified_atWARNINGlevelr:   )	cmf.snapshot_versionr   hasattrr(   CmfCustFieldr   rf   gdebug)
cust_fieldr   metaraw_cf_field_versionr:   s        rM   _cust_field_metazCmfCustomClass._cust_field_meta   s     	Az:.,,:,FJ""$&z3DdK'K23GH#GGU]fGgK#3 rh   c                      j                  |      }t        |t              r.t         fdt	        |j                         d       D              S t        |t        t        t        f      rS|D cg c]  } j                  |       }}t        |t              rt        t	        |t                    S t        |      S |S c c}w )u   Нормализовать значение для стабильного сравнения сигнатур пользовательских полей.c              3   J   K   | ]  \  }}|j                  |      f  y wri   )_normalize_cf_value).0keyitemrF   s      rM   	<genexpr>z5CmfCustomClass._normalize_cf_value.<locals>.<genexpr>   s-      C c--d34s    #c                     t        | d         S )Nr   )rm   )pairs    rM   <lambda>z4CmfCustomClass._normalize_cf_value.<locals>.<lambda>   s    DQRG rh   )r   )
r   r@   r   tuplesortedr?   rA   setr   repr)rF   r   r   
normalizeds   `   rM   r   z"CmfCustomClass._normalize_cf_value   s     

5!eW% !';T!U   edE3/0DIJD#11$7JJJ%%VJD9::$$	 Ks   0Cc                 H    t        | dd      }t        t        |d|            S )u   Проверить, что строка CmfCustField является удалённой с учётом wrapper-значений.r   Fr   )rf   rp   )r   raw_deleteds     rM   _is_deleted_cust_fieldz%CmfCustomClass._is_deleted_cust_field   s&     j-?GK+>??rh   
field_mapsc           	          ddl m}m}  |       }|D ]5  }|j                         D ]   } |t	        |dd            }|||kD  s|}" 7 |S )u  Посчитать новый снимок меты после точечной обработки строк БД.

        Базовая версия берётся из уже загруженных классов полей. Для удаления
        класса в памяти после применения delta уже не будет, поэтому версию удалённой
        строки нужно учесть здесь до изменения меты в памяти.
        r   )normalize_cf_versionread_cf_versionr:   N)r   r   r   valuesrf   )r^   r   r   r   snapshot_version	field_map
field_datafield_versions           rM   _cf_delta_snapshot_versionz)CmfCustomClass._cf_delta_snapshot_version   si     	O*,# 	5I'..0 5
 4WZI[]a5b c ,AQ1Q'4$5	5
  rh   Nc                    i }i }dd| j                   j                  gg}t        |       j                  |      }||j	                  dd|g       t
        j                  j                  |t        | j                        dd      D ]b  }t        j                  | j                  |      | j                         }| j                  |      r|||j                  <   T|||j                  <   d ||fS )u  Загрузить из БД строки пользовательских полей для точечной перезагрузки.

        При обычной догрузке читаем только строки новее снимка меты. Полный список
        нужен только при отсутствии снимка, когда безопаснее сверить всё состояние модели.
        cmf_model_name==r   >T)filterfieldsinclude_deletedTECHCOM_nocacher'   )	model_clsr   typer~   appendr(   r   slistrA   _CF_DELTA_DB_FIELDSr   rN   r   r   )r^   r}   active_fieldsdeleted_fieldsfield_filterfilter_changed_sincer   r   s           rM   _load_db_custom_fieldsz%CmfCustomClass._load_db_custom_fields   s     )41J1JKL#DzHHW+!2C9M NO --33#D445 $ $	 4 & 		BJ
 (11$2G2G
2S[_[i[i1jJ**:68Bz4457Aj334		B n,,rh   
field_namec                     |j                  d      sy|dd }| j                  j                  |      }t        |xr t	        |t
                    S )uN   Определить FK-компаньон relation-поля вида cf_x_id._idFN)endswithr   getrp   
issubclassCmfRelationBase)r   r   primary_nameprimary_fields       rM   _is_relation_companionz%CmfCustomClass._is_relation_companion  sM     ""5)!#2!((,,\:MPj&PQQrh   c                     | j                   j                  j                         D ci c]5  \  }}|j                  d      r| j	                  | j                   |      s||7 c}}S c c}}w )us  Вернуть только основные пользовательские поля текущей меты в памяти.

        FK-компаньоны вида ``cf_x_id`` не считаются самостоятельными полями:
        они создаются и удаляются вместе с основным relation-полем.
        cf_)r   r   r?   
startswithr   )r^   r   	field_clss      rM   _runtime_primary_cf_mapz&CmfCustomClass._runtime_primary_cf_map  sb     *.)>)>)D)D)F
%
I$$U+//
K 	!
 	
 
s   :A&r   c                     |j                   xs i j                         D ci c]  \  }}|| j                  vr|| c}}S c c}}w )u   Выделить пользовательскую часть UI-меты для сравнения с классом в памяти.)r   r?   _UI_META_SIGNATURE_SKIP)rF   r   r   r   s       rM   _ui_meta_signature_from_dataz+CmfCustomClass._ui_meta_signature_from_data"  sN    
  *117R>>@
U#555 J
 	
 
s   Ar   c                 f   t        |di       xs i }| j                  |      }|rt        t        j                  |d      nd}t        |di       xs i }i }|j	                         D ]I  \  }}|| j
                  v r|dk7  r+||v r'| j                  |      | j                  ||         k(  rE|||<   K |S )u  Выделить из класса в памяти только пользовательские отличия UI-меты.

        Метакласс поля добавляет дефолтные значения базового типа. Их нельзя
        считать изменением пользовательского поля, иначе delta будет видеть
        ложные обновления только из-за метаданных.
        r   Nr   )rf   _field_type_namer[   r   r?   r   r   )	rF   r   
field_metafield_type_namebase_cls	base_metaresultr   r   s	            rM   _ui_meta_signature_from_classz,CmfCustomClass._ui_meta_signature_from_class+  s     Y	26<"
..y9AP73::=VZHi4:	$**, 	 JCc111|#y(//6#:Q:QR[\_R`:aaF3K	  rh   attrc                     |dk(  rt        |dd      ryt        ||d      }|X||j                  xs i vrFt        |dd      }|rt        t        j                  |d      nd}|t	        ||      rt        ||      S |S )u   Достать атрибут сигнатуры из строки БД с учётом дефолтов базового типа поля.r,   r-   NTr   )rf   r   r[   r   r   )rF   r   r   r   r   r   s         rM   _signature_attr_from_dataz(CmfCustomClass._signature_attr_from_dataB  s     00WZI_ae5f
D$/=T**<*<*BC%j,EOETwszz?DAZ^H#$(?x..rh   c                     | j                   D ci c]  }|| j                  ||       }}| j                  |      |d<   | j                  |      S c c}w )u   Собрать сигнатуру строки БД для проверки изменений только в метаданных.r   )_CF_SIGNATURE_ATTRSr   r   r   )rF   r   r   signature_datas       rM   !_custom_field_signature_from_dataz0CmfCustomClass._custom_field_signature_from_dataO  sf    
 //
 #//
DAA
 
 %($D$DZ$Py!&&~66
s   Ac                 (   t        |dd      }t        |dd      }|r|st        |dd      S t        |t        t        f      rdnd}	 t	        |      j                  ||      }||k(  r|S t        |dd      S # t        $ r t        |dd      cY S w xY w)u   Определить generated-модель пользовательского справочника по runtime-классу поля.r   Nr'   r-   re   r   r_   )rf   r   	CmfM2MExtCmfRelationExtr   ra   	Exception)rF   r   r   owner_model
model_namer_   expected_model_names          rM   _choice_model_name_from_classz,CmfCustomClass._choice_model_name_from_classY  s     i)94@Y6
*9&<dCC$YN0KLRV	D"0J"G"`"` #a # ,,y"8$??	  	D9&<dCC	Ds   A8 8BBc                    | j                  |j                  |      }i d| j                  |      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      d	t        |d	d      d
t        |d
d      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      dt        |dd      t        |dd      t        |dd      t        |dd      t        |dd      |t	        |xs t        |dd            | j                  |      d}| j                  |      S )uq   Собрать сигнатуру класса в памяти для сравнения с метой из БД.r   r    Nr9   r   r'   r(   r*   r+   r.   r3   r4   r5   r&   r1   r2   r"   r6   r7   r8   r/   r0   r)   r,   F)r8   r/   r0   r)   r-   r,   r   )r   r   r   rf   rp   r   r   )rF   r   r-   r   s       rM   "_custom_field_signature_from_classz1CmfCustomClass._custom_field_signature_from_classl  s     #@@AUAUW`a
#..y9
wy)T:
 wy2CQG
 WY6	

 gi48
 wy)T:
 	3Et L
 wy)T:
 wy)T:
 	:t<
 	:t<
 gi48
 79mTB
 wy)T:
 	3Et L
  "796KT#R!
" (<WY])^#
$ ,39>[]a+bY6Ivt4y)T:$8+/$`	;XZ_(`, 88C5
8 &&~66rh   c                 h    | j                  |      xs  t        |dt        |      j                        S )u~   Вернуть системный тип поля для сообщений и сравнения формы хранения.r   )r   rf   r   rj   )rF   r   s     rM   _field_kind_from_classz%CmfCustomClass._field_kind_from_class  s0     ##I.l')\SWXaSbSkSk2llrh   c                 |    t        | dd      rt        | j                        S t        | dd      r| j                  fS y)uz   Вернуть модели-цели поля-связи из CmfCustomField или класса в памяти поля.r(   Nr'   r>   )rf   r   r(   r'   )r   s    rM   _relation_targetsz CmfCustomClass._relation_targets  s?     :x.**++:w-$$&&rh   c                    t        |t              rht        |dd      r3t        |dd      }t        |dd      }|r|sy| |j                          S | j                   |j                  j                          S t        |t
              rjt        |dd      }|syt        |dd      r t
        j                  | j                  |      S t        |dd      r t
        j                  || j                        S y)u_   Посчитать имя runtime-модели связи M2M по данным CmfCustomField.r/   Nr'   r)   r0   )r   CmfGenericM2Mrf   rY   r   CmfM2M_m2m_model_cls_name)r   r   r   r   r)   s        rM   _m2m_model_cls_name_from_dataz,CmfCustomClass._m2m_model_cls_name_from_data  s     j-0z7D1$Z$?
!*i>!$g&8&8&:%;<<**+J,A,A,L,L,N+OPPj&) Wd;Jz64011)2F2F
SSz7D111*i>R>RSSrh   c                 N   t        t        j                  |j                        }|j                  xs d}t        |t              r| j                  ||      }|j                  |dt        |dd      t        |dd      t        |dd      t        t        |dd      xs d      t        |d	d      |t        |      | j                  | j                  ||      fS t        |t              r"|j                   d
f}| j                  |      }n|j                  f}d}|j                  |||fS )u`   Описать физическую форму хранения поля по строке БД.r   m2mr0   Nr/   r'   r(   r>   r)   r   )rf   r[   r   r   r9   r   
CmfM2MBase#_custom_choice_model_name_from_datar   rp   r  r   r   r   r  )r^   r   r   r9   choice_model_nameruntime_columnsrelation_targetss          rM   _storage_shape_from_dataz'CmfCustomClass._storage_shape_from_data  s$   SZZ)>)>?
$449j*- $ H HU_ `%%
FD1
GT2
GT2gj(D9?R@
It4!&'224>>:zZ  j/2","7"7!8<>O#55jA)446O!%%IYZZrh   c                    | j                  |      }t        |dd      xs d}t        |t              r||j	                         }| j                  ||      }||dt        |dd      t        |dd      t        |dd      t        t        |dd      xs d	      t        |d
d      |t        |      |fS t        |t              r| df}| j                  |      }n|f}d	}||||fS )uy   Описать физическую форму хранения уже загруженного поля в памяти.r9   r   r  r0   Nr/   r'   r(   r>   r)   r   )
r   rf   r   r	  m2m_model_cls_namer   r   rp   r   r  )	rF   r   r   r   r9   m2m_model_namer  r  r  s	            rM   _storage_shape_from_classz(CmfCustomClass._storage_shape_from_class  s    ..y9!)->BGai,&99;N # A A*i X	640	7D1	7D1gi48>B?	9d3!&'  i1",S13O"44Y?)mO!/CSTTrh   c                 j    | j                  ||      }| j                  |      }||k(  ryd| d| d| S )u  Проверить, не меняет ли обновление физическое хранение поля.

        Точечный путь безопасен только для изменений метаданных. Если меняется
        тип колонки, ext-таблица, направление M2M или таблица связи, уходим на
        полный путь генерации/DDL.
        rQ   z*unsupported_cf_delta:update_storage_shape::z->)r  r  )r^   r   r   r   	old_shape	new_shapes         rM   _storage_shape_update_reasonz+CmfCustomClass._storage_shape_update_reason  sM     22:yI	11*=		!;J<qSUV_U`aarh   r   r_   c                     |sy	 t        |      j                  | j                  |      }||k(  S # t        $ r Y yw xY w)u   Проверить, что имя модели совпадает с generated-именем справочника этого поля.Fr   r   )r   ra   r   r   )r^   r   r   r_   r   s        rM   _is_generated_choice_targetz*CmfCustomClass._is_generated_choice_target  sU    	"0J"G"`"` #a # 000  		s   '1 	==c                 *    | j                  ||d      S )u_   Проверить generated-имя ext-справочника для CmfRelationExt/CmfM2MExt.re   r   )r  )r^   r   r   s      rM   _is_generated_choice_ext_targetz.CmfCustomClass._is_generated_choice_ext_target  s    //
Ju/UUrh   c                     | duxrD t        |t              xr2 t        |t               xr t        | dd      xs t        | dd      dk(  S )u   Понять по строке БД, что обычное M2M-поле использует пользовательский справочник.Nr-   r'   rc   )r   r  r   rf   )r   r   s     rM   _is_custom_choice_m2m_dataz)CmfCustomClass._is_custom_choice_m2m_data  s^     d" :v.z955 
$:DA O:w59NN	
rh   c                     |y	 t        |t              rt        |t              ry	 t	        |dd      }|sy	 t        |       j                  |      }||k(  S # t        $ r Y yw xY w# t        $ r Y yw xY w)u   Понять по runtime-классу, что обычное M2M-поле использует пользовательский справочник.NFr'   r   )r   r  r   r   rf   r   ra   )r   r   r   r   r   s        rM   _is_custom_choice_m2m_classz*CmfCustomClass._is_custom_choice_m2m_class  s     	i0Jy)4T 5U Y6
	"0J"G"`"`al"m 000  		  		s"    A A' 	A$#A$'	A32A3c                     | j                  ||      syt        |dd      xs t        |dd      }|dk(  r/t        |j                        j	                  | j
                        S |S )u   Вернуть имя модели пользовательского справочника для M2M-данных из БД.Nr-   r'   rc   r   )r  rf   r   r   ra   r   )r^   r   r   r   s       rM   r
  z2CmfCustomClass._custom_choice_model_name_from_data+  sg    ..z:FZ)?Fl'R\^egkJl
..!Z-B-BC\\]a]k]kllrh   c                    |rt        |dd      nt        | dd      }|rt        |dd      nt        | dd      }|rt        |dd      nt        | dd      }|rt        |dd      nt        | dd      }t        | t              r-|rt        |xs g       S |rt        |xs |r|g      S g       S g S t        | t              r|r|gS g S g S )u{   Собрать имена моделей, которые должны существовать для runtime M2M-поля.r'   Nr(   r0   r/   )rf   r   r  rA   r  )r   r   r   model_namesr0   r/   s         rM   _m2m_related_model_namesz'CmfCustomClass._m2m_related_model_names4  s     <FWZ$77S]_fhlKm
=Ggj(D9WU_aikoMp4>wz640GJX^`dDe6@
GT2gjZacgFhj-0K-2..KOJZLPPBPPIj&)#-J<525	rh   actionr   c                B   	 |xs$ t        t        j                  |j                        }t        |dd      xs | j                  |      }t        |dd      xs t        |dd      }| j                  ||      }|du xr | j                  ||| j                        }	|r|dk(  r*|(| j                  ||| j                        sdd| d| d	fS | j                  ||      }
|
r| j                  ||
      r||
d
fvrdd| d| d	fS t        |dd      }t        |dd      }|r|rdd| d| d| fS y|	r,t        |dd      }t        |dd      }|r|rdd| d| d| fS yt        |dd      rdd| d| d	fS t        |dd      rdd| d| d	fS |dv rdd| d| d	fS |dk(  r/|-t        |t              r| j                  ||      rdd| d| d	fS |dk(  r/|-t        |t              r| j                  ||      rdd| d| d	fS t        |t              rdd| d| d| fS t        |t               rdd| d| d| fS t        |t              r|rt        |dd      nt        |dd      }|rt        |dd      nt        |dd      }|s|sdd| d| d| fS | j#                  ||      }|D cg c]  }t        t$        |d      r| }}|r|rdd| d| d| fS yt        |t&              r!|rt        t$        |d      sdd| d| d| fS yt        |t(              rPt        |dd      xs t        |dd      xs g }|D cg c]  }t        t$        |d      r| }}|rdd| d| d| fS y# t        $ r t        |dd      }dd| d| d| fcY S w xY wc c}w c c}w )u   Проверить, поддерживает ли точечный путь add/update/delete для конкретного поля.r   unknownFzunsupported_cf_delta:r  Nr'   deletez:custom_choicerc   r0   r/   TrQ   r-   r,   )rc   rd   r(   )rf   r[   r   r   r   r   r  r  r   r
  r  r   r   r  r	  r   CmfBackrefBaser#  r(   CmfRelationCmfGenericRelation)r^   r   r   r$  r   r   kindr   custom_choice_m2m_datacustom_choice_m2m_classr  r0   r/   r"  rel_model_namemissings                   rM   _delta_support_statusz$CmfCustomClass._delta_support_statusE  s   	I"Pgcjj*:O:O&PJ
 z<6a$:U:UV`:aZ$7]7:wX\;]
!%!@!@Z!X$ S00z4>>R 	  "("!- <<T9dnn] 5fXQtfNSSS $ H HU_ `%;;DBST!*;=R)SS 5fXQtfNSSS:vt4DJ6E5 5fXQtfAdVLLL":vt4DJ6E5 5fXQtfAdVLLL:5t<1&4&OOO:<eD1&4&OOOKK1&4&OOOh&z>:88zJ1&4&OOOh&z:644T:F1&4&OOOj),1&4&$HHHj.11&4&$HHHj*-8B7:vt4PZ\bdhHiD:DGJ6'R\^egkJlEE 5fXQtfAdVLLL77
JOK<Gu.wW]_mosOt~uGu' 5fXQtfAdVLLLj+.WVZ%F 5fXQtfAdVLLL  
$67!*h=jU]_cAdjhjK<Gu.wW]_mosOt~uGu 5fXQtfAdVLLLM  	I:|Y?D1&4&$HHH	It v vs)   (M. -NNNN.#NNc                 (   | j                   |_        | j                   j                   d|j                   |_        t	        t        |di       xs i       |_        |j                  j                  dd       |j                  j                  dd       y)u   Дозаполнить runtime-класс поля метаданными, которые обычно ставит BaseModelMeta..r   r-   Nr,   )r   r   r   r   ro   rf   r   pop)r^   r   s     rM   _materialize_runtime_field_metaz.CmfCustomClass._materialize_runtime_field_meta  s|    #'>>	 '+~~'@'@&A9CWCWBX$Y	! Ir!B!HbI	4d;;TBrh   r   baseextrar7  r8  c                    |xs |j                   }|xs$ t        t        j                  |j                        }| j
                  j                  | j
                  j                   d| |j                  d|j                  xs dd}|j                  xs i j                         D ]  \  }}	|dv r|	||<    | j                  D ]  }
t        ||
d      }	|	|	||
<    |r|j                  |       t        ||f|      }| j                  |       |S )uo   Создать runtime-класс пользовательского поля из строки CmfCustomField.r3  Tr   )rk   rl   r    r!   r9   )r   r   r   r#   r$   r%   N)r   rf   r[   r   r   r   rk   rl   r    r9   r   r?   _RUNTIME_FIELD_ATTRSupdater   r5  )r^   r   r   r7  r8  runtime_namer   	namespacer   r   r   r   s               rM   _make_runtime_field_classz(CmfCustomClass._make_runtime_field_class  s    4z44E73::z/D/DE..33#~~::;1\NK!)))99>Q
	 &--3::< 	#JCkk"IcN	# -- 	(DJd3E "'	$	( U#{I>	,,Y7rh   rel_field_clsc                 *   t        |t              rM|j                   dt        |j                         d|j
                  |j                  ddt        |dd      d}nzt        |t              ri|j                   d|j                  xs g D cg c]  }t        |       d c}|j
                  |j                  d|j                  t        |dd      d}ny	t        |d
d      rd|d
<   | j                  ||j                   dt        |      S c c}w )uo   Создать скрытое FK-поле cf_x_id для relation-поля, если оно требуется.z ID.idFTr9   r   )r    foreign_keynullableuniquer3   indexr9   )r    foreign_keysrC  rD  r3   rE  r9   Nno_aclr   r6  )r   r*  r    r   r'   rC  rD  rf   r+  r(   rE  r>  r   CmfTUUID)r^   r   r?  r8  r   s        rM   _make_relation_id_field_classz,CmfCustomClass._make_relation_id_field_class  s(   m[1+334C8"-m.A.A"B!C3G)22'.. #*=:KQ#OE '9:+334C8TaThThTnln pZK
$;#<C!@ p)22'.. &,,#*=:KQ#OE =(E2"E(O--))*#.	 . 
 	
 !qs   Dc                 0   t        t        j                  |j                        }d}| j	                  ||      r| j                  ||      }|r||d}| j                  ||      }|j                  |i}| j                  ||      }||||j                  <   |S )ul   Создать основной runtime-класс поля и его служебные companion-поля.N)r'   r-   )r8  )	rf   r[   r   r   r  r
  r>  r   rI  )r^   r   r   r8  r  r   runtime_fieldsid_field_clss           rM    _runtime_fields_for_custom_fieldz/CmfCustomClass._runtime_fields_for_custom_field  s    SZZ)>)>?
**:zB $ H HU_ ` .,= 22:U2K	$//;99*iP#6BN<223rh   c                     t               g fd}t        j                  d       D ]
  } ||         || j                         S )u  Получить снимок загруженных моделей для проверки общих runtime-моделей.

        M2M/choice модель в памяти может использоваться полями других загруженных
        моделей. Если просмотр моделей падает, ошибка уходит в общий fallback
        точечной перезагрузки на полный reload.
        c                 p    | y t        |       }|v ry j                  |       j                  |        y ri   )idaddr   )r   markerloaded_modelsseens     rM   append_oncezACmfCustomClass._loaded_delta_models_snapshot.<locals>.append_once  s9     	]F~HHV  +rh   c                 .    t        t        | dd             S )Nr   )rp   rf   r   s    rM   r   z>CmfCustomClass._loaded_delta_models_snapshot.<locals>.<lambda>  s    d75RZ\`Ca>b rh   )r   r   iter_modelsr   )r^   rU  loaded_modelrS  rT  s      @@rM   _loaded_delta_models_snapshotz,CmfCustomClass._loaded_delta_models_snapshot  sO     u	, $//0bc 	&L%	& 	DNN#rh   c              #   @   K   | j                         E d{    y7 w)u\   Итерировать модели из снимка загруженных моделей.N)rY  )r^   s    rM   _iter_loaded_delta_modelsz(CmfCustomClass._iter_loaded_delta_models  s     55777s   c              #      K   || j                         }|D ]Q  }t        |dd      xs i j                         D ]-  }t        |t              s|j                         |k7  r(||f / S yw)u   Найти загруженные M2M/GM2M поля, использующие указанную runtime-модель связи.Nr   )r[  rf   r   r   r	  r  r^   r   rS  rX  r   s        rM   _iter_m2m_fields_for_model_namez.CmfCustomClass._iter_m2m_fields_for_model_name  s{       ::<M) 	.L%lHdCIrQQS .	!)Z8//1Z?"I--.	.s   A*A,c                    t        | j                  |            }t        fd|D              s,|j                  t	        dd      xs | j
                  f       t               }t               }|D ]s  \  }}t        |t              st	        |dd      r!|j                  t	        |dd      xs g        Et	        |dd      sSt	        |dd      }|sc|j                  |       u t        |      t        |      fS )u  Собрать стороны общей таблицы связи в памяти для GenericM2M.

        Одна таблица связи может объединять left/right поля из разных моделей,
        поэтому берём не только новое поле, но и уже загруженные поля с тем же
        именем модели в памяти.
        c              3   ,   K   | ]  \  }}|u   y wri   r>   r   rR   loaded_fieldr   s      rM   r   z:CmfCustomClass._generic_m2m_model_lists.<locals>.<genexpr>#       RL<9,R   r   Nr0   r(   r/   r'   )rA   r^  anyr   rf   r   r   r   r  r;  rQ  r   )	r^   r   r   loaded_fieldsleft_modelsright_models_loaded_modelrb  
left_models	     `      rM   _generic_m2m_model_listsz'CmfCustomClass._generic_m2m_model_lists  s     TAA*MNRMRR  ')5Et"L"^PTP^P^`i!jkeu+8 	0'M<lM:|VT2##GL(D$I$ORPw5$\7DA
OOJ/	0 k"F<$888rh   c                 F   t        | j                  |            }t        fd|D              s,|j                  t	        dd      xs | j
                  f       |D ]p  \  }}t        |t              rt        |t              r't	        |dd      }t	        |dd      }t	        |dd      r
|r|r||fc S t	        |dd      sf|si|sl||fc S  t	        t	        dd      dd      xs | j
                  j                  }t	        dd      }t	        dd      r||fS ||fS )u   Определить левую и правую модели общей таблицы связи в памяти обычного M2M.c              3   ,   K   | ]  \  }}|u   y wri   r>   ra  s      rM   r   z;CmfCustomClass._ordinary_m2m_model_sides.<locals>.<genexpr>7  rc  rd  r   Nr   r'   r0   r/   )
rA   r^  re  r   rf   r   r   r  r  r   )r^   r   r   rf  rX  rb  owner_model_namerelated_model_names     `     rM   _ordinary_m2m_model_sidesz(CmfCustomClass._ordinary_m2m_model_sides4  s6   TAA*MNRMRR  ')5Et"L"^PTP^P^`i!jk*7 	<&L,lF3z,P]7^&|\4H!(w!E|VT27GL^');;;|Wd38HM_)+;;;	< #796F#M|]abfjftftff$Y>9gt,%'777!333rh   c                 "   t        | j                  dd      }t        |t              rt        }| j                  ||      \  }}dd|d| j                  j                  |t        t        d|      t        t        ddd      t        t        d	|      t        t        d
dd      t        t        dd      t        t        dd      d}nt        }| j                  ||      \  }}	dd|d| j                  j                  |t        t        d|      t        t        dt        |       ddd      t        t        d|	      t        t        dt        |	       ddd      t        t        dd      t        t        dd      d}t        ||f|      S )uj   Создать runtime-класс таблицы связи для обычного M2M или GenericM2M.data_sourcesNFTu   Объект)r    r(   u   ID Объекта)r    rC  rE  u   Элементu   ID Элемента   Имя объекта)r    rC  u   Имя элемента)abstractr!   rr  _cf_delta_runtimerk   rl   r0   left_idr/   right_idleft_name_cacheright_name_cacheu   Левый Объект)r    r'   u   ID Левого ОбъектаrA  )r    rB  rC  rE  u   Правый Объектu    ID Правого Объектаu"   Имя левого объектаu$   Имя правого объекта)rf   r   r   r  r   rk  rk   Fieldr+  rH  	CmfStr256r   rp  r*  r   r   )
r^   r   r   rr  r   rg  rh  r=  left_model_nameright_model_names
             rM   _make_runtime_m2m_model_classz,CmfCustomClass._make_runtime_m2m_model_classJ  s   t~~~tDi/#H(,(E(EjR[(\%K! ,%)"nn77 *0.Q\] 3FQU]ab1;KT`a!(4ITX`de#(<S^b#c$))=Vae$fI #H040N0Nz[d0e-O-! ,%)"nn77 *k3LTcd <#.#?"@ D! {4OWgh!>#./?#@"A E! $)<`ko#p$))=cnr$s1I4 JY77rh   c                     | j                   j                  }t        |dd      }|t        |dd      }|r|d   nt        }ddlm}  |||      }|j                  |_        ||i|_        y)u   Привязать модель в памяти к текущему data_driver без нового подключения к БД.data_sourceNrr  r   )DataProvider)r  r'   )	r   dprf   r   cmf.data_providers.baser  data_driver_dd__dp__)r^   runtime_modeldata_providerr  rr  r  runtime_providers          rM   !_bind_runtime_model_data_providerz0CmfCustomClass._bind_runtime_model_data_provider}  sn    ))m]DA"=.$GL-9,q/?QK8'K}U,88 +-=>rh   c                    t        | j                  dd      }ddd|d| j                  j                  |t        t        dddddd      t        t
        ddd	
      t        t        ddddd      t        t        dddddd	      t        t        dddd      t        t        ddddd      t        t        dddd      d}t        |t        f|      S )u\   Создать runtime-класс generated-справочника для custom-choice M2M.rr  NFTu)   Идентификатор объектаu3   Автоматически генерируется)r    r2   rC  primary_keyr4   r3   rs  always)r    rE  	load_modeu   Сортировкаr   )r    rE  r3   rC  r.   u   Кодu3   Код в реальном мире из жизни)r    rD  r2   r4   rC  r  u:   Код родителя в каскадном выборе)r    r4   rC  rE  u   Скрытьu!   Конфигурация поля)rt  	api_allowr!   rr  ru  rk   rl   rP  r   ordernocodechoice_parent_id
cmf_hiddencust_field_conf_id)rf   r   rk   rz  rH  r{  CmfIntCmfStr64CmfBoolr   r   )r^   r   rr  r=  s       rM    _make_runtime_choice_model_classz/CmfCustomClass._make_runtime_choice_model_class  s   t~~~tD(!%..33&CM  )-DD\de.  M" !&T!  & #(;#e9
	t Ji88rh   c                    t        t        |d      }|du xs t        |dd      }|r"| j                  |      }t        t        ||       t        t        j                  ||       | j                  |       | j                  j                  j                  }|j                  j                  |d       |j                  |j                  j                  vrDt        |dd      r7| j                  |j                        }| j                  ||j                         	 |j!                  |       |S # t"        $ r t$        j'                  d| d        w xY w)u   Зарегистрировать runtime-модель пользовательского справочника в models и SA-метаданных.Nru  Fenginez4cf-delta: failed to prepare runtime choice SA model 	EXCEPTIONr   )rf   r(   r  setattrr[   r  r   r  r  models_registryr4  	tablenamedb_metatables_reflect_sa_table!_copy_reflected_table_to_metadatadp_modelr   r   r   )r^   r   r  rebuild_runtime_modelr  reflected_tables         rM   _register_runtime_choice_modelz-CmfCustomClass._register_runtime_choice_model  s6   
D9T! B}&95A 	 ! AA*MMFJ6

J6..}=nn''33##''
D9""+*=*=*D*DDQ\^fhlIm"44]5L5LMO22?KDWDWX	  /   	GGJ:,W_jGk	s   D+ +%Ec                     | j                         }|D ]P  }t        |dd      xs i j                         D ],  }t        |t        t
        f      st        |dd      |k(  s+  y R y)u|   Проверить, используется ли справочник другими загруженными полями.r   Nr'   TF)rY  rf   r   r   r	  r   r]  s        rM   _choice_model_still_usedz'CmfCustomClass._choice_model_still_used  sp    ::<) 	 L%lHdCIrQQS  	!)j/-JK9gt4
B	 	  rh   c                     t        | t              syt        | t              syt	        | dd      xs i }h dt        |      k  S )u   Проверить, похож ли класс на generated-модель пользовательского справочника.Fr   N>   rP  r  r   r  r  r  r  )r@   r   r   r   rf   r   r  r   s     rM    _is_generated_choice_model_classz/CmfCustomClass._is_generated_choice_model_class  sC     -.-3$7=2hlopvlwwwrh   c                 r    t        | dd      }|dk(  xs$ |j                  d      xs |j                  d      S )u   Проверить, что generated-модель справочника пришла из разрешённого autogen/custom модуля.rk   rQ   custom_modelscustom.ztmp.__autogen_modelsrf   r   )r  module_names     rM   &_generated_choice_model_module_allowedz5CmfCustomClass._generated_choice_model_module_allowed  sF     m\2>?* >%%i0>%%&<=	
rh   rn  c                 @    |syt        | dd      j                  |      S )u   Проверить, что generated-модель справочника относится к текущей модели-владельцу.Tr   rQ   r  )r  rn  s     rM   _choice_model_references_ownerz-CmfCustomClass._choice_model_references_owner  s%      }lB7BBCSTTrh   c                    t        t        |d      }|yt        |dd      xsL | j                  |      xr9 | j                  |      xr& | j	                  || j
                  j                        }|sy| j
                  j                  j                  }|j                  j                  |d       t        |      }||j                  j                  v r2|j                  j                  |j                  j                  |          t        t        |d      |u rt        t        |       t        t         j                  |d      |u rt        t         j                  |       yy)u  Удалить модель пользовательского справочника из памяти.

        Удаляем только модели, созданные точечным путём или похожие на сгенерированные модели
        текущей модели-владельца. Ручные пользовательские модели и модели других владельцев не трогаем.
        Nru  F)rf   r(   r  r  r  r   r   r  r  r  r4  r   r  r  removedelattrr[   r^   r   r  can_unregisterr  
table_names         rM    _unregister_runtime_choice_modelz/CmfCustomClass._unregister_runtime_choice_model  s.     
D9 M#6> 55mD b??Nb77t~~G`G`a 	 nn''33##''
D9 ,
,,333&&{':':'A'A*'MN6:t,=FJ'3::z40MACJJ
+ Brh   c                    | j                   j                  j                  }|j                  }i }t	        |dd      xs i j                         D ]N  }t	        |dd      }|s|j                  dd      d   }||j                  v s||v r;| j                  |      ||<   P |j                         D ]  \  }}	| j                  |	|        y)uc   Отразить FK-таблицы, нужные SQLAlchemy для runtime-модели связи.r   NrB  r3  rt   r   )r   r  r  r  rf   r   rX   r  r  r?   r  )
r^   r  r  r  reflected_tablesr   	fk_targettarget_tabler  r  s
             rM   %_prepare_runtime_m2m_fk_target_tablesz4CmfCustomClass._prepare_runtime_m2m_fk_target_tables#  s    nn''33%%!-4@FBNNP 	RI	=$?I$??3215Lw~~-AQ1Q-1-C-CL-Q\*	R ,<+A+A+C 	M'J22?GL	Mrh   c                 @   |j                         }t        t        |d      }|du xs t        |dd      xs t        |t              }|r#| j                  ||      }t        t        ||       t        t        j                  ||       | j                  |       | j                  |       | j                  j                  j                  }|j                  j                  |d       	 |j                  |       y# t         $ r t"        j%                  d| d        w xY w)uj   Зарегистрировать runtime-модель M2M/GenericM2M в models и SA-метаданных.Nru  Fz1cf-delta: failed to prepare runtime M2M SA model r  r   )r  rf   r(   r   r  r~  r  r[   r  r  r   r  r  r  r4  r  r   r   r   )r^   r   r   r  r  r  s         rM   _register_runtime_m2m_modelz*CmfCustomClass._register_runtime_m2m_model3  s   113

D9T! 4}&95A4)]3 	
 ! >>z9UMFJ6

J6..}=22=Ann''33##''
D9	  / 	GGG
|T\gGh	s   &C8 8%Dc                 f    | j                         }t        d | j                  ||      D              S )u   Проверить, используется ли runtime-модель связи другими загруженными M2M-полями.c              3   &   K   | ]	  \  }}d   yw)TNr>   )r   ri  
_field_clss      rM   r   z7CmfCustomClass._m2m_model_still_used.<locals>.<genexpr>M  s     y5M:4ys   )rY  re  r^  )r^   r   rS  s      rM   _m2m_model_still_usedz$CmfCustomClass._m2m_model_still_usedJ  s0    ::<y9]9]^hjw9xyyyrh   c                     t        | t              syt        | t        t        f      syt        | dd      ryt        | dd      xs i }h dt        |      k  S )un   Проверить, похож ли класс на generated-модель таблицы связи M2M/GM2M.Frt  r   N>   r0   r/   rv  rw  rx  ry  )r@   r   r   r   r   rf   r   r  s     rM   _is_generated_m2m_model_classz,CmfCustomClass._is_generated_m2m_model_classO  sW     -.-+|)DE=*e4$7=2^beflbmmmrh   c                     |syt        | dd      j                  |      ryt        | dd      xs i }dD ]:  }|j                  |      }t        |dd      |k(  r y|t        |dd      xs d	v s: y y
)u   Проверить, что runtime-модель связи относится к текущей модели-владельцу.Tr   rQ   r   N)r0   r/   r'   r(   r>   F)rf   r   r   )r  rn  r   	side_name
side_fields        rM   _m2m_model_references_ownerz*CmfCustomClass._m2m_model_references_owner[  s      =,3>>?OP$7=2* 	II.Jz7D15EEGJ$$G$M2N	 rh   c                    t        t        |d      }|yt        |dd      xs9 | j                  |      xr& | j                  || j                  j
                        }|sy| j                  j                  j                  }|j                  j                  |d       t        |      }||j                  j                  v r2|j                  j                  |j                  j                  |          t        t        |d      |u rt        t        |       t        t        j                  |d      |u rt        t        j                  |       yy)u  Удалить M2M-модель из памяти и SA-меты.

        Удаляем только модели, созданные точечным путём или сгенерированные модели, которые
        явно ссылаются на текущую модель-владельца. Это защищает чужие общие модели.
        Nru  F)rf   r(   r  r  r   r   r  r  r  r4  r   r  r  r  r  r[   r  s         rM   _unregister_runtime_m2m_modelz,CmfCustomClass._unregister_runtime_m2m_modelk  s     
D9 M#6> 22=A _44]DNND]D]^	 	 nn''33##''
D9 ,
,,333&&{':':'A'A*'MN6:t,=FJ'3::z40MACJJ
+ Brh   column_namesc                    t        |      D ]N  }|| j                  j                  vr	 | j                  j                  | j                  j                  |          P y# t        $ ry | j                  j                  j                  |d       | j                  j                  D cg c]  }|j                  |k7  r| nc c}w c}| j                  j                  dd Y w xY w)u  Удалить устаревшие cf-колонки только из метаданных SQLAlchemy в памяти.

        В используемой версии SQLAlchemy штатное удаление может не синхронизировать
        оба внутренних контейнера ColumnCollection, поэтому резервная ветка чистит их явно.
        N)rA   _columns_datar  r   r4  _all_columnsr   )sa_tabler  column_namecolumns       rM   _remove_sa_columnsz!CmfCustomClass._remove_sa_columns  s      - 
	K("3"3"9"99!!(():):)@)@)MN	
	
  !!''++K>)1):):)G)G5%{{k1 5 5!!..q1s   2AAC %B>= C C c                     | y| j                   |u ry| j                  |j                  v r(|j                  |j                  | j                            t	        | dd      xs t	        | d      } ||       y)u   Скопировать отражённую таблицу в рабочую SQLAlchemy metadata при необходимости.Nto_metadata
tometadata)metadatar   r  r  rf   )r  target_metadatacopy_methods      rM   r  z0CmfCustomClass._copy_reflected_table_to_metadata  st     "##6?#9#99""?#9#9/:N:N#OPo}dCmw`lGmO$rh   r  c                     | j                   j                  j                  }t        |dd      syddl} |j
                         } |j                  ||d|j                  dd      S )uV   Отразить таблицу из БД во временную SQLAlchemy metadata.r  Nr   TF)autoloadautoload_withextend_existingresolve_fks)r   r  r  rf   
sqlalchemyMetaDataTabler  )r^   r  r  r  scratch_metas        rM   r  z CmfCustomClass._reflect_sa_table  sf    nn''33{Hd3*z**,z%,, 
 	
rh   rK  removed_runtime_namesc                    | j                   j                  j                  }|j                  }|j                  j                  | j                   j                        }g g i t        |      d}|j                         D ]S  \  }}t        |t              rg }	t        |dd      r|	j                  |j                         |	j                  t        |dd      xs g        t        |dd      xs d}
|
dkD  rB| j                   j                   d|
d}|j                  j                  |      }|}|| j                  |      }||d	   |<   nA||j                   j"                  vr)t        |t$              r| j                  |      }||d	   |<   |^||j                   j"                  vrFt        |t$              s6|j'                  | j                   |d      }||d
   j                  |||f       |	D ]E  }|j)                  dd      d   }||j                  vs'||d	   vs/| j                  |      |d	   |<   G |j'                  | j                   ||      }|0|.||j                   j"                  vr|d   j                  ||f       |	D ]E  }|j)                  dd      d   }||j                  vs'||d	   vs/| j                  |      |d	   |<   G V |S )u  Подготовить план изменений метаданных SQLAlchemy без мутаций модели.

        План строится до блокировки и до изменения меты в памяти. Если выяснится, что нужен
        полный путь или состояние поменял другой поток, мы не оставим частично изменённые
        SA-таблицы в памяти.
        )main_columns_to_addext_columns_to_addr  r  rB  NrF  r9   r   _ext02dr  r  r3  rt   r  )r   r  r  r  r  r   r  r   r?   r   r	  rf   r   rB  extendr  r  r  r   _make_sa_columnrX   )r^   rK  r  r  r  r  planr<  r   
fk_targetsr9   ext_table_name	ext_tablemetadata_ext_table	sa_columnr  r  s                    rM   _prepare_sa_deltaz CmfCustomClass._prepare_sa_delta  s    nn''33%%>>%%dnn&>&>?#%"$ "%()>%?	
 (6';';'= &	b#L))Z0Jy-6!!)"7"78giFL"M%i1BAFK!O"$(NN$<$<#=T/RUAV!W#NN..~>	%."$)-)?)?)O&?QD+,^<!););)A)AAjQZ\kFl)-)?)?)O&?QD+,^<&2(0B0K0K0Q0QQ *9o F + ; ;DNNIW[ \I ,1299><Yb:cd!+ fI#,??3#:1#=L#7>>9lRVWiRj>jAEAWAWXdAe/0>f #33DNNI|\I$)=,V^VgVgVmVmBm*+22L)3LM' b	(sA6q9w~~5,dSeNf:f=A=S=ST`=aD+,\:bG&	bN rh   sa_delta_planc                    | j                   j                  j                  }|j                  }|j                  j                  | j                   j                        }|j                  d      xs
 t               }||r| j                  ||       t        |j                  j                               D ]C  \  }}|j                  | j                   j                   d      s/|s2| j                  ||       E |j                  di       j                         D ]  \  }}| j                  ||        |j                  dg       D ]N  \  }}	}
|j                  j                  |      }|%|	|j                  j                  vs>|j                  |
       P |j                  j                  | j                   j                        }|E|j                  dg       D ]/  \  }	}
|	|j                  j                  vs|j                  |
       1 yy)u   Применить подготовленный план SA-метаданных после проверки гонки под блокировкой.r  Nr  r  r  r  )r   r  r  r  r  r   r  r   r  rA   r?   r   r  r  r  append_column)r^   r  r  r  
main_tabler  r  r  r  r<  r  r  s               rM   _apply_sa_delta_planz#CmfCustomClass._apply_sa_delta_plan  s   nn''33%%^^''(@(@A
 - 1 12I J Sce!&;##J0EF$()=)=)?$@ 	I J$$(@(@'A%FGLa''2GH	I ,9+<+<=OQS+T+Z+Z+\ 	M'J22?GL	M3@3D3DEY[]3^ 	3/Ji**:6I$Y=O=O=U=U)U''	2	3 ^^''(@(@A
!+8+<+<=RTV+W 8'iz':':'@'@@,,Y78 "rh   r   c                    | j                   j                  D ]R  }t        |dd      }|st        j                  j                  |d       t        j                  j                  |d       T | j                   j                  j                  }|j                  j                  |d       |j                  j                          y)u   Инвалидировать кеши метаданных/запросов после атомарного cf-delta изменения.r   N)r   __mro__rf   APPall_models_metar4  cache_cust_field_config_schemer  r  r  cached_queriesclear)r^   r   cparent_namer  s        rM   _invalidate_caches_for_modelz+CmfCustomClass._invalidate_caches_for_model  s    '' 	JA!!\48K##''T:2266{DI		J nn''33##''
D9""((*rh   r   runtime_mapc                    |h}|j                  |      }|r&t        |t              r|j                  | d       |S | d| j                  j
                  v r3| j                  | j                  | d      r|j                  | d       |S )u   Вернуть имена runtime-полей, которые нужно удалить вместе с основным полем.r   )r   r   r   rQ  r   r   r   )r^   r   r  runtime_namesold_field_clss        rM   _old_runtime_names_for_primaryz-CmfCustomClass._old_runtime_names_for_primary  s    %#5ZGc23  ~S!T^^%:%::t?Z?Z[_[i[inzm{{~k  @Ac23rh   
old_fields
new_fieldsc                 p    t        |      t        |j                               z   }t        d |D              S )u~   Понять, затрагивает ли delta relation/M2M поля и требует ли пересчёта RelationCache.c              3   D   K   | ]  }|xr t        |t                y wri   )r   
CmfRelBase)r   r   s     rM   r   z9CmfCustomClass._relation_cache_touched.<locals>.<genexpr>  s'      
 ;*Y
;;
s    )rA   r   re  )r^   r	  r
  touched_fieldss       rM   _relation_cache_touchedz&CmfCustomClass._relation_cache_touched  s9    j)D1B1B1D,EE 
+
 
 	
rh   c           
         t         j                  d| j                  j                   dt	        j
                          d       | j                  j                  }	 |t        j                  }| j                  |      \  }}| j                  ||      }| j                         }t        |      t        |      z
  }|t        |      t        |      z
  }nt        |      t        |      z  }t        |      t        |      z  D 	ch c]-  }	| j                  ||	         | j                  ||	         k7  r|	/ }
}	t        |      D ]>  }	| j                  ||	   d|	      \  }}|r t         j                  d	| d
       d|fc S  t        |
      D ]y  }	| j                  ||	   d|	      \  }}|s t         j                  d	| d
       d|fc S | j!                  |	||	   ||	         }|s[t         j                  d	| d
       d|fc S  t        |      D ]N  }	| j                  |j#                  |	      ||	   d|	      \  }}|r0t         j                  d	| d
       d|fc S  |s'|s%|
s#|t        _        t         j                  dd       yi }i }t        ||
z        D ],  }	| j%                  ||	         }|||	<   |j'                  |       . |j)                         D 	ci c]  \  }	}t+        ||	   t,              r|	||	     }}	}t               }t               }g }t               }t               }t        |      D ]  }	| j/                  |	|      }|j'                  |       |j'                  |       |j#                  |	      }|j1                  |       |s\t+        |t,              sm|j3                  |j5                                | j7                  |	|| j                        s| j9                  |	|      }|s|j3                  |        t        |
      D ]C  }	|j'                  | j/                  |	|             |j1                  |j#                  |	             E | j;                  ||      }|j)                         D 	ci c]  \  }	}|	| j                  |       }}	}t=        |       j?                  |      }|5  | j                         }|j)                         D 	ci c]  \  }	}|	| j                  |       }}	}||k7  r,d| }t         j                  d	| d
       d|fcddd       S |D ]^  }| j                  j@                  jC                  |d       | j                  jD                  jG                  di       jC                  |d       ` |jI                         D ]o  }|j)                         D ]Z  \  }}|| j                  j@                  |<   tK        |jD                        | j                  jD                  jG                  di       |<   \ q t        |      D ]%  } | jM                  |       r| jO                  |        ' t        |      D ]%  }| jQ                  |      r| jS                  |       ' |jI                         D ]i  }| j7                  |j                  || j                        r/| j9                  |j                  |      }|r| jU                  |       | jW                  |       k | jY                  |       | j[                  |       | j                  j@                  jI                         D ch c]  }t]        |       }!}| j_                  ||      }"	 |"rt`        jb                  je                          n t`        jb                  jg                  ||!       |t        _        ddd       t         j                  dtm        |       dtm        |
       dtm        |       d       yc c}	w c c}}	w c c}}	w c c}}	w c c}w # th        $ rI}#dt=        |#      jj                   }t         j                  d| d       d|fcY d}#~#cddd       S d}#~#ww xY w# 1 sw Y   xY w# th        $ r;}#t         j                  dd       ddt=        |#      jj                   fcY d}#~#S d}#~#ww xY w)ue   Точечный путь reload_model(delta_only=True), основанный на данных БД.zcf-delta: called for z pid=INFOr   Nr}   rQ  )r$  r   zcf-delta: fall back reason=r   Fr;  r'  z5cf-delta: success added=0 changed=0 removed=0 (no-op)r(  zcf_delta_race:r   zrelation_cache_failed:z
cf-delta: r  zcf-delta: success added=z	 changed=z	 removed=z.cf-delta: DB-backed delta exception, fall backzdb_delta_failed:)7r   r   r   r   osgetpidr  r   r   r   r   r   r   r   r   r1  r  r   rM  r;  r?   r   r	  r  r   rQ  r  r  r   r  r   rz   r   r4  r   ry   r   ro   r  r  r  r  r  r  r  r  rP  r  r(   RelationCachebuild_fields_cachedrop_stale_for_modelr   rj   rZ   )$r^   r}   r   active_db_fieldsdeleted_db_fieldsr   r  added_namesremoved_namesr   changed_names	supportedreasonnew_runtime_fieldsruntime_fields_by_primaryrK  new_m2m_fieldsruntime_removed_namessa_removed_runtime_namesold_touched_fieldsm2m_model_names_to_unregister choice_model_names_to_unregisterold_runtime_namesr  r  r  r   planned_prestatelockcurrent_runtime_mapcurrent_prestater<  r   	valid_idsrelation_cache_touchedes$                                       rM   _apply_cf_field_deltaz$CmfCustomClass._apply_cf_field_delta$  s	   	'(A(A'B%		}U]cd^^..
V	@$ # 4 4262M2M\i2M2j//#>>?OQbc668K./#k2BBK$ #K 037G3H H !$$5 6[9I I-01A-BSEU-U)99:J::VW::;z;RST M  %[1 )
$($>$>$Z0Z %? %Q!	6 GG9&B)GT &=() %]3 
)
$($>$>$Z0
 %? %T!	6 GG9&B)GT &=(:: 0 <k*>UWGG9&B)GT &=(
) %]3 )
$($>$>%))*5{:7NW_fp %? %r!	6 GG9&B)GT &=() }]'7$OW]^!#(*%$[=%@A :
!%!F!FGWXbGc!d8F)*5")).9: 3L2Q2Q2S.JnZ8*E N:66N 
 %(E!'*u$!#,/E)/2u,$]3 T
$($G$G
T_$`!%,,->?(//0AB +
 ;"))-8 Zz%J155m6V6V6XY77
MSWSaSab,0,N,Nz[h,i),<@@ARST %]3 G
%,,T-P-PQ[]h-ij"))+//**EFG !223EG_`M .9->->-@ )J	 DCCINN   
 :66zBD 38&*&B&B&D# 2E1J1J1L$-
I  G G	 RR$  $ $'77-j\:FGG9&B)GT &=38 38 %: \LNN))--lDANN**55hCGGVZ[\ '@&F&F&H pN3A3G3G3I p/i>G--l;X\]f]n]nXo..99(BGUpp
 #))F"G GJ55jA:::FG *00P)Q Q%889JK==>OPQ "0!6!6!8 @I77	8L8LiY]YgYgh,0,N,NyOcOcen,o), ??@QR44Y?@ ))-811*=<@NN<Q<Q<X<X<Z[yR	][	[)-)E)EFXZl)m&)-,,??A,,AA*iX (8$g38j GG*3{+;*<Ic-FXEYYbcfgtcubvw   IT6 $D \ ! )5d1g6F6F5GHFGGj1GE &=(c38 38\)]38 38t  	@GGDKGX,T!W-=-=,>???	@s  B*a: 2_=7,a: $a: A
a: a: +a: ;a: a: ')a: Aa: %#`Ba: a: -<a: *a: ?B	a: `$a: $a.'`*a.-	a: 7Da.<2a./Ca.`a.-A`.a.9Aa: =a: a.	a+"5a&a+a.	a: &a++a..a73a: :	b>0b93b>9b>c                     |t        d      | j                  j                  |      }| | |      }|| j                  |<   |S )u   Вернуть кешированный CmfCustomClass для модели или создать новый экземпляр.u   CmfCustomClass.get_or_create: model_cls is None (вероятно cmf_model_name не найден через get_model_by_name)r   )
ValueError_instances_by_modelr   )rF   r   instances      rM   get_or_createzCmfCustomClass.get_or_create  sZ     c  **..y9Y/H19C##I.rh   c                     t        |j                  t        j                         j	                  d      ||      }t        d|       y)u  
        Полная синхронизация кастом полей модели в кластере после изменения их настроек
        - event CmfCustomClass:custom_field_sync -> custom_fields_gen_meta()
        - custom_fields_gen_meta()
            - per host lock and read custom field config
            - check_meta_version
            - save_changes()
                - patch db fixme !!! commit/rollback
                - save custom/modules/fields/MODEL_NAME.py
            - apply_changes()
                - do autogen
                - event CmfCustomClass:reload_model -> reload_model_handler()

        - reload_model_handler()
            - load and patch custom model
            - cler APP caches
            - !!! CMF_CACHE.flush_db() fixme !!! per process, drop locks...
            - models.RelationCache.build_fields_cache() fixme !!! per process

        z%Y%m%d%H%M%S)r   meta_versionforcer    CmfCustomClass:custom_field_syncN)ro   r   datetimenowstrftimecmf_emit_server_event)rF   r   r8  r   _kwargsdatas         rM   custom_field_syncz CmfCustomClass.custom_field_sync  s>    ,  ++!00@_np 	@$Grh   r9  )channelr?  c           
      ^   ddl m} ddl}t        j                  t
        j                        dz  dz  }|j                         }t        j                  d| d       |j                  j                  d| dd	d	
      }|5  t        j                  d       |j                         r5|j                         | d   k(  rt        j                  d       	 ddd       y|j                  | d          t               }t!        t"              | d      }t%        |      }	dd|j&                  gg}
| d   s;|
j)                  ddd|	j*                  D cg c]  }|j&                   c}gg dg       t"        j,                  j/                  |
g dd      D ]h  }|j0                  s-t$        j3                  |      ||j4                  j6                  <   nd|	_        |j:                  sPd|_        |j=                  d       j |s|	j8                  rg }|j?                         D ](  }|j)                  t@        jC                  ||             * t        j                  d|        |	jE                  |       t        j                  d       |j                  j                  dd	d	
      5  t        j                  d        |	jG                          ddd       t        j                  d!       |	jI                          ddd       t        j                  d"       yc c}w # 1 sw Y   QxY w# 1 sw Y   0xY w)#u   Метод обновляет файлы с метой на всем кластере

        Args:
            data (dict): _description_
        r   )REDIS_DBNtmpcustom_meta_versionuJ   custom_fields_gen_meta берем блокировку per instance lock::z)::cmf__base_model__custom_fields_gen_metazlock::i  )timeoutblocking_timeoutu<   custom_fields_gen_meta Блокировка полученаr7  z-CmfCustomClass:custom_field_sync: already genr   r1  r   r   r8  ORr   zNOT IN)dirtyr   T)r   parent	cmf_ownerT)r   r   r   F)	only_datar   z(custom_fields_gen_meta run merge_fields uu   custom_fields_gen_meta берем блокировку global lock::cmf__base_model__custom_fields_gen_meta_run_alterz7lock::cmf__base_model__custom_fields_gen_meta_run_alteruN   custom_fields_gen_meta блокировка получена. run save_changesz$custom_fields_gen_meta apply_changeszcustom_fields_gen_meta done)%cmf.apprC  socketpathlibPathconfigPROJECT_DIRgethostnamer   r   redisr)  exists	read_text
write_textro   varsr(   rB   r   r   custom_fieldsr   rA   r   r   r   r   
is_changedrI  saver   r   rN   merge_fieldssave_changesapply_changes)r?  r>  rC  rN  meta_ver_pathhostname
redis_lock	ui_fieldsrF   custom_class_filterfr   rY  custom_field_datas                  rM   custom_fields_gen_metaz%CmfCustomClass.custom_fields_gen_meta  s    	%V%7%785@CXX
 %%'	\]e\f  gP  Q  	R^^((6(;d)eos  GK(  L
 ,	-GGRS##%-*A*A*CtNG[*[GH,	- ,	- $$T.%9:Iv,tL12C)C8L($?@G=Xl>X>X'Y'YZ) + ,
 %1166"7$( 7  4

 "--7E7V7VWa7bIjoo334 /3L+##',J$OOdO34 L33 ")2)9)9); a%!(()A)ABS[^)A)_`a B=/RS))-8  P  Q^^((+bmq  EI(  J 0GGlm --/0 >?**,Y,	-Z 	
-.= (Z20 0Q,	- ,	-sF   AL#A0L#LA?L#CL#!&L-L#L#L 	L##L,c                    t               }dgddd}| j                  |      }|	dd|gg|d<   t        j                  j                  di |D ]q  }t        |j                  d|j                        }||vs)|j                  |       | j                  t        j                  |      	      }|j                  d|
       s y)u  Перезагрузить только модели, чьи пользовательские поля изменились после снимка меты.

        changed_since фиксируется один раз для всего batch: иначе первая обработанная
        модель может продвинуть APP.snapshot_version, и следующие модели потеряют
        свои строки из той же догрузки после форка.
        r   T)r   r   r   Nr   r   r   r   r1  
delta_onlyr}   r>   )r   r~   r(   r   r   rf   r   rQ  r5  r   get_model_by_namereload_model)rF   r}   processed_modelsslist_kwargsr   custom_fieldr   custom_models           rM   reload_modelszCmfCustomClass.reload_models,  s     5'(##

  #AA-P+'8#?S&T%UL""//55EE 	XL !<!<g|GbGbcJ!11 $$Z0"007;T;TU_;`0a))T)W	Xrh   CmfCustomClass:reload_modelc                 L   ddl }t        j                  d |j                          d|         t        j
                  }t        j                  t        j                  | d               }|j                  d|       t        j                  d	 |j                                 y)
u   Обработать событие кластера и точечно перезагрузить мету указанной модели.r   Nz7======================= reload_model_handler START PID=z data=r   r1  Tri  z5======================= reload_model_handler END PID=)r  r   r   r  r  r   rB   r5  r   rk  rl  )r?  r>  r  r}   rc  s        rM   reload_model_handlerz#CmfCustomClass.reload_model_handlerD  s     		I)"))+V\]a\bcd,,%33g>W>WX\]mXn>o3p!!T!O	G			}UVrh   r   c                    || _         d| _        g | _        d| _        d| _        g | _        d| _        | j                         | _        | j                  j                  d      | _
        d| _        | j                   j                  j                  j                  | j                   j                        D ci c]  }|d   j                  d      r|d   | c}| _        | j                  j!                         }| j                   j                  j                  j#                  | j                         }| j                  j%                  |       |j%                  |       t'        t(              }| j                   j*                  j-                         D cg c]   }|j.                  j                  d      s|" c}D ]  }|j1                  |j.                        }d}	d}
d}t3        |t4        t6        f      rst9        t:              j1                  |j<                  j1                  d            }|r| j                   j                  j                  j?                  |      retA               }nZt3        |tB              r'|j.                   d}	|	|v r|jE                  |	      nd}n#|j.                  }	|	|v r|jE                  |	      nd}t:        jF                  j1                  |j.                  dg	      }|s8tH        jK                  | jM                  |      |
      }|| j                  jO                  |       }d| _         | j                  D ci c]  }|j.                  | c}| _        yc c}w c c}w c c}w )u   Инициализировать рабочее состояние синхронизации пользовательских полей модели.Nz.bkFr   r   r'   r   r   )r   r   r   T)(r   current_fieldsrY  
db_columnsrZ  add_to_db_fieldscustom_fields_index_get_model_file_pathmodel_file_pathwith_suffixmodel_bk_pathr  r  inspect_table_columnsr  r   copyinspect_ext_columnsr;  r   rA   r   r   r   r   r   r)  r	  rX  r(   r   	has_tablero   r   r4  r   r   rN   r   r   )r^   r   r  rw  ext_colsfields_ui_groupsrJ   r   r   r$   _db_table_namedb_field_info	cmf_modelr   r   s                  rM   __init__zCmfCustomClass.__init__O  s   ">B=?59*.68HL 7;7P7P7R595I5I5U5UV[5\
 ..++77MMdnnNfNfg
f~((/ 6NF"

 __))+
 >>$$00DDT^^Tx((# 't, &*^^%:%:%A%A%Cfq||G^G^_dGe!f 	'I(,,Y-A-ABI!N!N M)nj%AB L,,Y->->-B-B7-KL	!2!2!>!>!H!H!S$(FMI7$-$8$8#9!=BPT^B^
~ >dh!*!5!5BPT^B^
~ >dh,,00i6J6JTWSX0YJ'11$2G2G
2S[d1eJ (""))*5"&3	'6 Y]XjXj#k*J$9$9:$E#k W
  g6 $ls   3!M* M/1M/M4r   c                    | j                   D ci c]  }|j                  | }}|D ]=  }|j                  |j                        }|s$| j                   j                  |       d| _        |}|j                  | j
                  j                  vrX|j                  | j                  vs|j                  dz   | j                  vs| j                  j                  |       d| _        | j
                  j                  |j                     }t        |d      r |j                  |j                  k7  rd| _        t        |d      s|j                  |j                  k7  s7d| _        @ yc c}w )u  
        Получим список новых полей, которые надо создать.
        Соберём новые группы на основе системного значения и списка полей.
        Поля удалять нельзя, т.к. данные могут быть бесценны. TODO удалять, если данных нет.
        Tr   r*   r+   N)rY  r   r   r   rZ  r   r   rw  rx  r   r*   r+   )r^   r   o
cur_fieldsr   ro  fields          rM   r\  zCmfCustomClass.merge_fields  s2    04/A/AB!allAoB
B  	+J%>>**?*?@L""))*5"&)&&dnn.C.CC**$//AlF]F]^cFckokzkzFz))00>&*DO--l.E.EF5),1E1E1V&*DO5"45,:W:W[`[q[q:q&*DO#	+ Cs   E)c           	         | j                   j                  j                  }|j                  | j                   |      rb||j                  j
                  vrIddl} |j                  ||j                  d|j                  d       |j                  j                          y| j                   j                  }|j                  | j                   |       |j                         }	 | d}	 |j                  d| d| d	| d
       |j                          |j1                          |j                         }	 t+        j,                  d|        |j                  d| d| d| d       |j                          t+        j,                  d| d       |j                  d| d       |j                  d| d| d| d       |j                          t+        j,                  d| d       	 |j1                          | j                   j                  j                  j                  j                          y# t         $ re}|j#                          t%        |j&                  d      r3|j&                  j(                  dk(  rt+        j,                  d| d       n Y d}~d}~ww xY w# t.        $ r |j#                           w xY w# |j1                          w xY w# t.        $ r* |j#                          t+        j2                  d|         w xY w# |j1                          | j                   j                  j                  j                  j                          w xY w)uj   Создание extension-таблицы с двухфазным заполнением строками.)r  ext_nor   NTr  r  r  _id_fkeyzALTER TABLE z ADD CONSTRAINT z FOREIGN KEY (id) REFERENCES z(id) ON DELETE CASCADEpgcode42710z_ensure_ext_table(): FK u5    уже существует — пропускаемuJ   _ensure_ext_table(): фаза 1 — массовое заполнение zINSERT INTO z (id) SELECT id FROM z# t WHERE NOT EXISTS (SELECT 1 FROM z e WHERE e.id = t.id)uR   _ensure_ext_table(): фаза 2 — финальная синхронизация u    с локомzLOCK TABLE z IN SHARE MODEz_ensure_ext_table(): u%    создана и заполненаuF   _ensure_ext_table(): Ошибка заполнения таблицы )r   r  r  r  r  r  r  r  r  r  r   r  add_custom_ext_modelSessionexecutecommitSAProgrammingErrorrollbackr   origr  logginginfor   close	exception)	r^   r  r  r  r  
base_tablesconstraint_namer.  s	            rM   _ensure_ext_tablez CmfCustomClass._ensure_ext_table  s2   nn''33  4>>& I[%8%8%?%??! 
  "K$7$7!1C1C$(
 **002^^--
 	((@ !	!/ 09O		">"2 3&&5%6 733=,>TV
 
 GGI !	A LLefteuvwII~. /"", .33A2BBWY
 HHJ LLmn|m}  ~L  M  NIIJ<~>?II~. /"", .33A2BBWY
 HHJLL00@@efg GGI NN))88>>@Y & 

1668,'1ILL#;O;L  MB  "C  D D	  	JJL	 GGI2  	JJL fgufvwx	
 GGI NN))88>>@sW   #J% )+H4 5B5K 4	J"=AJJ% J""J% %K  K K3LL A
Mc                 :   | j                   j                  }|j                  dkD  rE|j                  d}| j                   j                   d| }| j                  ||j                         | j                   j                  j
                  j                  |||       y)ui   Создать колонку в основной или extension-таблице по `custom_table_no`.r   r  r  N)r   r  r9   r  r  r  add_custom_column)r^   r   r  r   r  ext_nns         rM   _add_custom_column_routedz(CmfCustomClass._add_custom_column_routed  s    ~~//%%)"2237F"nn667tF8DL""<1K1KL%%77kS]^rh   c                    | j                   D ]  }t        t        j                  |j                        }t        |t              r
t               |j                  rl|j                  r]t        |t              r:| j                  j                  j                  j                  |j                         nt        |t              rw| j                  j                  j                  j                  |j                         |j                    d}t        j                  j"                  }| j%                  |||       -| j                  j                  j                  j'                  |j                         | j                  j                    |j                   }| j                  j                  j                  j)                  |       t        |t*              r| j                  j                    |j                   j-                          }t        |t              s!| j/                  | j                  ||      xs |}| j                  j                  j                  j)                  |       _t        |t0              r*|j                    d}t        j                  j"                  }n|j                   }| j%                  |||        | j3                          y)uP   Создадим поля в базе и сохраним файл модели.r   N)rx  rf   r[   r   r   r   r)  NotImplementedr-   r,   r   r   r  r  add_custom_choice_model_extr   r   rH  r  add_custom_choice_modeladd_custom_m2m_modelr	  rY   r  r   _write_custom_model_file)r^   r   r   r  r   s        rM   r]  zCmfCustomClass.save_changes  s%   // 	PJ Z-B-BCJ*n5$&&0099!*i8))55QQR\RqRqr#J?))55QQR\RqRqr)3)>)>(?s&C%(ZZ%8%8
66z;PZ[ ))55MMjNmNmn$(NN$=$=#>z?^?^>_!`JNN%%11FFzRJ
3 $ 9 9::;P;P;[;[;]:^_
!*i8!%!C!CDNNT^`j!k!yoyJ!!--BB:NJ8!+!6!6 7s; ZZ00
(33**:{JO=	PF 	%%'rh   c                    ddl m} 	 d }	 t        j                  d        |        t        j                  s  |d	d
| j                  j                   i       yy# t        j
                  $ r t        j                  d       | j                  rU| j                  j                         r;t        j                  d       | j                  j                  | j                         nW| j                  rJ| j                  j                         r0t        j                  d       | j                  j                          n t        j                  d        |         w xY w)u~   Запустить autogen и разослать событие перезагрузки модели по процессам.r   )r=  c                  8   t        j                  t        j                         } dD ]C  }| dz  |z  }|j	                         st
        j                  d|        |j                          E t
        j                  d       t        j                  g dd       y )N)z__autogen_models.pyz__autogen_models_tmp.pyz__autogen_models.tsrD  u6   CmfCustomClass.apply_changes: удаляем мету uF   CmfCustomClass.apply_changes: генерируем новую мету)z/usr/bin/envpython3z	manage.pyautogenT)check)
rO  rP  rQ  rR  rU  r   r   unlink
subprocessrun)project_pathautogen_filenameautogen_file_paths      rM   applyz+CmfCustomClass.apply_changes.<locals>.apply$  s    "<<6+=+=*>@L$k / $0$67G$G!$++-GGTUfTghi%,,.	/ GG\]NNNVZ[rh   uX   CmfCustomClass.apply_changes: пытаемся перегенерировать метуuG   Ошибка применения новой кастом модели.u=   Есть backup file попробуем откатиться.uA   Попробуем откатиться удалив custom file.u]   CmfCustomClass.apply_changes: Повторный запуск apply() после откатаrr  r   N)cmf.includer=  r   r   r  SubprocessErrorr  r  r}  rU  r  renamer{  r  import_moder   r   )r^   r=  r  s      rM   r^  zCmfCustomClass.apply_changes   s	   5Y
	\ 	GGnoG }}!"?BRTXTbTbTmTmAno  )) 	gh!!d&8&8&?&?&A\]""))$*>*>?%%$*>*>*E*E*G`a$$++-GGstG	s   A C>Ec           	         ddl m} ddlm} ddl}ddlm} t        j                  d |j                                 |rY| j                  |      \  }}	|r(t        j                  d |j                                 yt        j                  d	|	 d
       t        | j                        j                  t        j                   dd      j                  dd      dd }
t        j                   t        j"                  |
             d}t        j                  d|        t        j$                  j'                  |t        |            }t        j$                  j)                  |      }|t*        j,                  |<   |j.                  j1                  |       t        j                  d       t        j                  d       t2        j4                  j6                  j8                  D ]  }t;        ||      }t=        t2              |   }g }g }|j>                  D ]O  }|jA                  d      s||j>                  vr|jC                  |       |j>                  |   |j>                  |<   Q  ||j>                        D ]A  }|jA                  d      s||j>                  vs$|jC                  |       |j>                  |= C i }t2        j4                  jE                  dd|ggddg      D ]3  }|jF                  jH                  xs d||jJ                  jH                  <   5 |j>                  jM                         D ]R  \  }}|jA                  d      s|jO                  d      r||vr|dd |v r|dd }n|}|jQ                  |d      |_#        T |jR                  |_)        |jT                  |_*        t        j                  d| d| d|         t=        |      D ]  }|jA                  | jV                  jX                   d      s?|jA                  | jV                  jX                   | jV                  jX                   d      sjt[        t2        |t;        ||              t        j                  d       i t\        _/        i t\        _0        | jV                  jb                  jd                  jf                  jQ                  | jV                  jX                        r?| jV                  jb                  jd                  jf                  | jV                  jX                  = | jV                  jb                  jd                  jh                  jk                          | jV                  jb                  jd                  }| jV                  jl                  }||jn                  jp                  v r|jn                  jp                  |   }ts        | jV                  j>                  ju                               }tE        |jv                  jx                        D ch c]7  }|jJ                  jA                  d      r|jJ                  |vr|jJ                  9 }}|rv|D ](  }|jv                  jz                  j}                  |d       * |jv                  jx                  D cg c]  }|jJ                  |vr| c}|jv                  jx                  dd ddl?}| jV                  j>                  jM                         D ]  \  } }!t;        |!d d      }"|"s|"j                  dd!      d   }#|#|jn                  jp                  v rD	  |j                  |#|jn                  d"|j                  d"#       t        j                  d$|# d%|          |st        j                  st        j                  d)       |j                          t        j                  d*       t2        j                  j                          t        j                  d+ |j                                 yyyc c}w c c}w # t        $ r! t        j                  d&|# d'|  d(       Y Tw xY w),u}  Перезагрузить пользовательские поля модели.

        delta_only=True включает точечное обновление меты в памяти без полного
        importlib.reload сгенерированных модулей. changed_since передаётся из
        пакетной догрузки после форка и ограничивает чтение БД строками новее снимка
        меты. При ошибке или неподдержанном типе используется полный путь перезагрузки.
        r   )	CMF_CACHE)r  N)models_orig_pathui   CmfCustomClass.reload_model: НАЧАЛО - перезагружаем мету в инстансе, PID=r  u7   CmfCustomClass.reload_model: delta_only УСПЕХ PID=zcf-delta: fall back, reason=r   r   /rQ   r3  r   r  uP   CmfCustomClass.reload_model: загружаем модуль custom_models из uW   CmfCustomClass.reload_model: модуль custom_models успешно загруженu   CmfCustomClass.reload_model: начинаем обновление кастомных полей для всех моделейr   r   r   r   r9   )r   r   r   z#CmfCustomClass.reload_model: model=z
 cf_added=z cf_removed=Cfu[   CmfCustomClass.reload_model: сбрасываем кеш меты для фронтендаrB  rt   Tr  z1CmfCustomClass.reload_model: reflected FK target z for field uT   CmfCustomClass.reload_model: не удалось отразить FK-таблицу u    для поля r  uE   CmfCustomClass.reload_model: сбрасываем CMF_CACHE.flushdb()ul   CmfCustomClass.reload_model: пересчитываем кеш полей RelationCache.build_fields_cache()uj   CmfCustomClass.reload_model: ЗАВЕРШЕНО - мета успешно перезагружена, PID=)HrM  r  r  r  cmf.make_modelsr  r   r   r  r/  rm   r{  replacerQ  rR  	importlibreloadimport_moduleutilspec_from_file_locationmodule_from_specsysmodulesloaderexec_moduler(   r   r   r*   rf   rX  r   r   r   rA   r9   r   r   r?   r   r   r   ui_group_fieldsr   r   r  r  r  r  r  r  r  r  r   r  r  r  r   keysr  r  r  r4  r  rX   r  r  r   r  flushdbr  r  )$r^   only_reloadrj  r}   r  r  r  r  successr  module_pathr  specr  r   rp  r   addedremovedr   custom_fields_metacfr   r7  r  r  r  actual_field_namescolstale_namesr   r  fnamefclsr  r  s$                                       rM   rl  zCmfCustomClass.reload_modelJ  s    	&4	{  }F|~  }F  }F  }H  |I  J  	K"88}8UOGVQR[RTR[R[R]Q^_`GG26(;9GM$../776;M;M:Na8PRTU]]^acfghkikl00=> &	bcsbtuv~~55k3GWCXY!77=#0K .	ij 	
  R  	S --<<DD *	nJ"=*=LVZ0IEG*11 S
((/!)9)99Z03?3F3Fz3RI$$Z0	S
 #9#3#34 5
((/JlFYFY4YNN:.!((4	5 "$))..)4<= 12 /  R 574F4F4L4L4QPQ"277==1	R *3)9)9)?)?)A P%
I((/ #++E2 *2D D *3B3E E)#2)0B0F0FtQ0OI-P" !- 4 4I(4(D(DI%GG9*ZPUwVbcjbklmU*	n\ }- 	PJ$$(A(A'B"%EF*J_J_cgcqcqc|c|b}  C  M  M  X  X  ~Y  Y[  a\  K]
GM:,NO	P 	
mn -/* >>((88<<T^^=V=VW!!--==dnn>W>WX 	%%44::< nn''33NN,,	++222"**11)<H!$T^^%:%:%?%?%A!B$():):)G)G$H 88&&u-#((BT2T K  ' <D%%++//d;< $,#4#4#A#A5xx{2 5!!..q1 	>>00668 	KE4mT:I$??3215L{22999 
   +"5"5!1C1C$(
 KL>Ydejdklm	( 1== GG[\ GG  C  D  335GG  A  BK  BD  BK  BK  BM  AN  O  P $1{M50  jkwjx  yJ  KP  JQ  R%  s   
<`	`-A`&`=<`=r    c                 @   |s!t         j                  j                  dd       | j                  |      }t	        d      D ]4  }d| }|s|j                  d      r|d| z  }|| j                  vs2|c S  t         j                  j                  d| d	| d
d       y )NuM   У настраиваемого поля не указано названиеTrU   d   r   r   rR   u#   Такое имя уже есть: ())r[   r\   r]   rC   ranger   rw  )rc  r    	name_basennr   s        rM   calc_field_namezCmfCustomClass.calc_field_name  s    KK!!"qy}!~ 009	* 	"Byk*JY''."h&
!8!88!!	" 	 CI;aPWyXYZbfgrh   c                 R    t        | dd      }dj                  d |D              }|S )NruT)language_codereversedrQ   c              3   `   K   | ]&  }|j                         r|j                         nd  ( yw)rR   N)isalnumlower)r   r  s     rM   r   z1CmfCustomClass.caption_to_name.<locals>.<genexpr>  s#     HaQYY[	c9Hs   ,.)r
   rW   )r    r   s     rM   rC   zCmfCustomClass.caption_to_name  s)    tdCwwH4HIrh   c                     | j                   D ]5  }t        |dd       }t        |t              s!|j	                  d      s3|c S  y )Nr   Cmf)r  rf   r@   rm   r   )r   r   base_class_names      rM   r   zCmfCustomClass._field_type_name  sD     ")) 	'H%hdCO/3/O4N4Nu4U&&	'rh   c                    t         j                  d| j                          t        j	                  t        j                  t        j                  j                  t        j                  d      g            }d |j                  d<   |j                  d      }| j                  j                  D ]  }|j                  j!                  d      r n d}| j                  j#                         r%| j                  j%                  | j&                         | j&                  j)                  |j+                  | |	             | j&                  j%                  | j                         y)
u^   Дампим кастомные поля в файл используя шаблон и jinja2uP   _write_custom_model_file: Пишем новую кастомную модель zcmf/templates)r  c                 h    t        | t              r!dj                  | j                  dd            S | S )Nz'''{}'''z'''z""")r@   rm   formatr  )xs    rM   r   z9CmfCustomClass._write_custom_model_file.<locals>.<lambda>  s.    ]ghikn]oz/@/@5RWAX/Y uv rh   quotezcmf_model.tmpltr  N)r!   
base_model)r   r   r{  jinja2EnvironmentFileSystemLoaderr  pathrW   rQ  rR  filtersget_templater   	__bases__rk   r   rU  r  r}  rW  render)r^   	jinja_envtemplater  s       rM   r  z'CmfCustomClass._write_custom_model_file  s   	bcgcwcwbxyz&&**BGGLL9K9K_,]+^_ ' a	 &w	'"))*;<..22 	J((33I>	 J&&(  ''(:(:;%%hooTjo&YZ!!$"6"67rh   c                    t        j                  t        j                   d| j                  j
                   d      }| j                  j                  D ]  }|j                  j                  d      rXdj                  |j                  j                  d      dd       }t        j                  t        j                   d| d      } nYdj                  |j                  j                  d      dd       }t        j                  t        j                   d| d      } n |j                  d	d	
       |dz  }|j                         s)|j                  d      5  	 ddd       t        d|        t        j!                  d      }dj                  |j#                  | j                  j$                        D cg c]  }|j'                          c}      }|| dz  S # 1 sw Y   xY wc c}w )uY   Возвращает путь к файлу где будут кастомные поляz/custom/modules/z/fieldsr  r  r3  Nz/custom/T)parentsexist_okz__init__.pywu   Создан z[A-Z][a-z0-9]+rR   z.py)rO  rP  rQ  rR  r   	ui_moduler  rk   r   rW   rX   mkdirrU  openprintrecompilefindallr   r  )r^   fields_dir_pathrF   postfixpkg_init_file_pathname_word_rewordmodel_filenames           rM   rz  z#CmfCustomClass._get_model_file_path  s   !,,&*<*<)==MdnnNfNfMggn'op>>++ 		C~~((3((3>>#7#7#<Sb#AB"),,&2D2D1EQwiw/W"X ((3>>#7#7#<Sb#AB"),,&2D2D1EXgYV]/^"_		 	dT:,]:!((*#((- M"4!567zz"23L<P<PQUQ_Q_QjQj<k"lD4::<"lm.!1555 
 #ms   G!;G-!G*c                    i }t         j                  j                  ddg      D ]=  }|j                  |vrg ||j                  <   ||j                     j	                  |       ? |j                         D ]{  \  }}t        j                  |      } | |      }|D ]C  }t        j                  | j                  |      |      }|j                  j	                  |       E |j                          } y )NTr   )is_dirtyr   r1  r   )r(   r   rA   r   r   r?   r   rk  r   rN   r   rY  r  )	rF   fields_to_model_namero  r   rY  r   
custom_clsr  r   s	            rM   write_custom_fields_from_dbz*CmfCustomClass.write_custom_fields_from_db.  s    !"//44dC54Q 	SL**2FFDF$\%@%@A !<!<=DD\R		S *>)C)C)E 	2%J11*=Iy1J, <+55c6J6J;6W_h5i
((//
;< //1	2rh   ri   )NN)r   N)FF)FFN)nrj   rk   rl   __doc__r   r|   r   rw   rx   rr   rz   r~   r   r:  	frozensetr   staticmethodr   r   ro   r   r   rp   r   r   rm   r   r   r   r   r   CmfTypeMetar   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r  r  r
  r   r#  r1  r5  r>  rI  rM  rY  r[  r^  rk  rp  r~  r  r  r  r  r  r  r  r  r  r  r  r  r  r  r   r  r  r  r  r  r  r  r  r/  r3  r5  r@  on_server_eventrg  rq  rt  r   r   r  r\  r  r  r]  r^  rl  r  rC   r   r  rz  r  r>   rh   rM   rB   rB      s   @&/&:#!GOG G
 C C (o{Iy/+<o((4E-	)
 
)  6 6   "   @d @ @
 d3;N6O  $ !%-).tC4G/H$sTbObJc/c)d-2 Rc Rd R R
c;.>)? 
 
n 
 
 
 k d  , 
> 
 
 
 7> 7 7 @s @{ @WZ @ @$ 7; 7 7B m{ ms m m    ^ Yd il  ([> [e [6 U3 U; USX U U8bb 'b #	b (+	b"1c 1s 1TW 1cg 1V# V3 VSW V 

~ 

; 

[_ 

 

 1 1 1]a 1 1$n Ze jm  [ n `deh`i  $ *.%)P&P #P
 P P  c	*PdC C C  $& 	
   $/>
 
Wb 
B> dSVXcScNd $t 28
.# 
.93 9; 9SXY]^aYbdhildmYmSn 924C 4K 4TYZ]_bZbTc 4,18 18 18f
?=93 =9~ 0	3 	4 	 x4 x x 
 
 
 U UPT U U,3 ,4 ,:M [ T .z z z
 	n 	n 	n S T  , , ,8 3s8   $ 	% 	%
C 
 7S+5E0F 7_bcf_g 7lp 7r8$ 84 8.
+s 
+t 
+3 TRUWbRbMc hklohp 
${2C 
QUVY[fVfQg 
lp 
Z@5s;K Z@x   H H4 ?@=/T =/ A =/~ X X. :;W < W9l$y/ 9lv+4#7 +4JAX_%(N&pTYPv hs hs h h   
 'K ' '8*60 2 2rh   rB   )/r  r  r  r  rO  r  r  collectionsr   collections.abcr   r:  r   r  gevent.lockr   r  r   dataclassesr   sqlalchemy.excr	   r  transliterater
   typingr   r   r   r   r   r   
cmf.fieldsr[   
cmf.modelsr  cmf.utilr   cmf.models.base_modelr   r   r   r   r   r   r   rB   r>   rh   rM   <module>r%     s      	   
 # #     ! A " 9 9       R2 R2 R2l2 2rh   