
    D6i'                         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mZmZ ddlmZ d	d
lmZ d	dlmZmZmZmZ d Zd Zd ZddZddZd ZddZd Zy)    )defaultdict)groupbyN)NoInspectionAvailable)object_session)ForeignKeyConstraintMetaDataTable   )
QueryChain   )	has_index)_get_class_registryget_column_key
get_mapper
get_tablesc                    t        |      }t        | j                  j                        D ci c]  \  }}| j                  j                  j                         |   t        ||j                  j                        r t        ||j                  j                        n.t        ||j                  |j                        j                         c}}S c c}}w N)r   	enumerate
constraintelementscolumnsvalueshasattrcolumnkeygetattrget_property_by_column)fkobjmapperindexelements        j/home/azureuser/techstart-app/venv/lib/python3.12/site-packages/sqlalchemy_utils/functions/foreign_keys.pyget_foreign_key_valuesr$      s    _F
 ((>(>?	 E7 	$$&u-3**+ 07sGNN<N<N/OS&77GKKL	M  s   BCc                 8    t        | d       } t        | d       S )a  
    Return a groupby iterator that groups given foreign keys by table.

    :param foreign_keys: a sequence of foreign keys


    ::

        foreign_keys = get_referencing_foreign_keys(User)

        for table, fks in group_foreign_keys(foreign_keys):
            # do something
            pass


    .. seealso:: :func:`get_referencing_foreign_keys`

    .. versionadded: 0.26.1
    c                 B    | j                   j                  j                  S r   )r   tablenamer   s    r#   <lambda>z$group_foreign_keys.<locals>.<lambda>,   s    8L8L8Q8Q     r)   c                 .    | j                   j                  S r   )r   r'   r)   s    r#   r*   z$group_foreign_keys.<locals>.<lambda>-   s    S^^-A-A r+   )sortedr   )foreign_keyss    r#   group_foreign_keysr/      s    ( ,,QRL<!ABBr+   c                    t        | t        j                        r| g}nt        |       }t	               }| j
                  j                  j                         D ]~  }||vs|j                  D ]h  }t        |t        j                  j                  j                        s2|j                  D ](  t        fd|D              s|j                         * j  |S )a,  
    Returns referencing foreign keys for given Table object or declarative
    class.

    :param mixed:
        SA Table object or SA declarative class

    ::

        get_referencing_foreign_keys(User)  # set([ForeignKey('user.id')])

        get_referencing_foreign_keys(User.__table__)


    This function also understands inheritance. This means it returns
    all foreign keys that reference any table in the class inheritance tree.

    Let's say you have three classes which use joined table inheritance,
    namely TextItem, Article and BlogPost with Article and BlogPost inheriting
    TextItem.

    ::

        # This will check all foreign keys that reference either article table
        # or textitem table.
        get_referencing_foreign_keys(Article)

    .. seealso:: :func:`get_tables`
    c              3   @   K   | ]  }j                  |        y wr   )
references).0tr   s     r#   	<genexpr>z/get_referencing_foreign_keys.<locals>.<genexpr>Z   s     @Ar}}Q/@s   )
isinstancesar	   r   setmetadatatablesr   constraintssqlschemar   r   anyadd)mixedr:   referencing_foreign_keysr'   r   r   s        @r#   get_referencing_foreign_keysrB   0   s    < %"E""u&&--/ =#// =
j"&&--*L*LM(11 =@@@488<=== $#r+   c           	        
 | j                   |j                   k7  rt        d      t        |       }t        |       }|D ]  
t	        
|       }t	        
|      }
fd|j                         D        }
j                  j                  j                         j                  t        j                  |       j                  |j                         D ci c]  \  }}|j                  | c}}      }	|j                  |	        yc c}}w )a  
    Merge the references of an entity into another entity.

    Consider the following models::

        class User(self.Base):
            __tablename__ = 'user'
            id = sa.Column(sa.Integer, primary_key=True)
            name = sa.Column(sa.String(255))

            def __repr__(self):
                return 'User(name=%r)' % self.name

        class BlogPost(self.Base):
            __tablename__ = 'blog_post'
            id = sa.Column(sa.Integer, primary_key=True)
            title = sa.Column(sa.String(255))
            author_id = sa.Column(sa.Integer, sa.ForeignKey('user.id'))

            author = sa.orm.relationship(User)


    Now lets add some data::

        john = self.User(name='John')
        jack = self.User(name='Jack')
        post = self.BlogPost(title='Some title', author=john)
        post2 = self.BlogPost(title='Other title', author=jack)
        self.session.add_all([
            john,
            jack,
            post,
            post2
        ])
        self.session.commit()


    If we wanted to merge all John's references to Jack it would be as easy as
    ::

        merge_references(john, jack)
        self.session.commit()

        post.author     # User(name='Jack')
        post2.author    # User(name='Jack')



    :param from_: an entity to merge into another entity
    :param to: an entity to merge another entity into
    :param foreign_keys: A sequence of foreign keys. By default this is None
        indicating all referencing foreign keys should be used.

    .. seealso: :func:`dependent_objects`

    .. versionadded: 0.26.1

    .. versionchanged: 0.40.0

        Removed possibility for old-style synchronize_session merging. Only
        SQL based merging supported for now.
    z+The tables of given arguments do not match.c              3      K   | ]>  \  }}t        j                  j                  j                  |j                        |k(   @ y wr   )r   r   r'   cr   )r3   r   valuer   s      r#   r5   z#merge_references.<locals>.<genexpr>   s=      
U BMM''))3773u<
s   AAN)__tablename__	TypeErrorr   rB   r$   itemsr   r'   updatewherer7   and_r   r   execute)from_tor.   session
old_values
new_valuescriteriar   rF   queryr   s             @r#   merge_referencesrU   _   s    ~ b...EFFU#G/6L +B6
+B3

(..0

 MM&&(U277H%&Vj6F6F6HI
USWWe^IJ 	
 	 Js   Dc                 *   |t        |       }t        |       }t        g       }t        | j                        }t        |      D ]  \  }}t        |      }|j                         D ]  }	 t        j                  |      }|j                  }	||j                  v s4|	r||	j                  v rE|j                  |      j                  t        j                  t!        |||              }
|j"                  j%                  |
         |S # t        $ r Y w xY w)aE  
    Return a :class:`~sqlalchemy_utils.query_chain.QueryChain` that iterates
    through all dependent objects for given SQLAlchemy object.

    Consider a User object is referenced in various articles and also in
    various orders. Getting all these dependent objects is as easy as::

        from sqlalchemy_utils import dependent_objects


        dependent_objects(user)


    If you expect an object to have lots of dependent_objects it might be good
    to limit the results::


        dependent_objects(user).limit(5)



    The common use case is checking for all restrict dependent objects before
    deleting parent object and inform the user if there are dependent objects
    with ondelete='RESTRICT' foreign keys. If this kind of checking is not used
    it will lead to nasty IntegrityErrors being raised.

    In the following example we delete given user if it doesn't have any
    foreign key restricted dependent objects::


        from sqlalchemy_utils import get_referencing_foreign_keys


        user = session.query(User).get(some_user_id)


        deps = list(
            dependent_objects(
                user,
                (
                    fk for fk in get_referencing_foreign_keys(User)
                    # On most databases RESTRICT is the default mode hence we
                    # check for None values also
                    if fk.ondelete == 'RESTRICT' or fk.ondelete is None
                )
            ).limit(5)
        )

        if deps:
            # Do something to inform the user
            pass
        else:
            session.delete(user)


    :param obj: SQLAlchemy declarative model object
    :param foreign_keys:
        A sequence of foreign keys to use for searching the dependent_objects
        for given object. By default this is None, indicating that all foreign
        keys referencing the object will be used.

    .. note::
        This function does not support exotic mappers that use multiple tables

    .. seealso:: :func:`get_referencing_foreign_keys`
    .. seealso:: :func:`merge_references`

    .. versionadded: 0.26.0
    )rB   r   r   r   	__class__r/   listr   r7   inspectr   inheritsr:   rT   filteror__get_criteriaqueriesappend)r   r.   rP   chainclassesr'   keysclass_r    parent_mapperrT   s              r#   dependent_objectsre      s   L 3C8S!GrNE!#--0G),7 ,tDznn& 	,FF+ #OOM%%=+?+?"?f-44FFM$<= $$U+	,, L ) s   )D	DDc                    g }g }| D ]  }|j                   |v r|j                  |j                          g }t        |j                   j                        D ]  \  }}|j                   j                  |   j
                  }	|j                  t        |t        ||            t        |t        j                  t        |            j                  |	      j                        k(          |j                  t        j                  |         |S r   )r   r_   r   r   r   r   r   r   r7   rY   typer   r   rL   )
rb   rc   r   rS   visited_constraintsr   subcriteriar!   r   foreign_columns
             r#   r]   r]     s    H />>00""3>>2&s~~'='=> 	ME6 ^^44U;BBNvv >?JJtCy)@@PTT	 	-./  Or+   c                 |   t               }t        | dd      }||t        d      t        t              }| j
                  j                         D ]`  }t        |||xs |      }|j                  D ]=  }t        |t              st        |      r ||j                     j                  |       ? b t        |      S )z
    Finds all non indexed foreign keys from all tables of given MetaData.

    Very useful for optimizing postgresql database and finding out which
    foreign keys need indexes.

    :param metadata: MetaData object to inspect tables from
    bindNzLEither pass a metadata object with bind or pass engine as a second parameter)autoload_with)r   r   	Exceptionr   rX   r:   rb   r	   r;   r6   r   r   r(   r_   dict)r9   enginereflected_metadatarl   r;   
table_namer'   r   s           r#   non_indexed_foreign_keysrs   )  s     "8VT*D|0
 	

 d#Koo**, ;
j"4DNFS++ 	;Jj*>?Z(EJJ'..z:	;; r+   c                     | j                   D ]5  }t        |j                  j                               t        |      k(  s3|c S  y r   )r;   rX   r   r   )r'   r   r   s      r#   get_fk_constraint_for_columnsru   J  s=    '' 

""))+,W=r+   r   )collectionsr   	itertoolsr   
sqlalchemyr7   sqlalchemy.excr   sqlalchemy.ormr   sqlalchemy.schemar   r   r	   query_chainr   databaser   ormr   r   r   r   r$   r/   rB   rU   re   r]   rs   ru    r+   r#   <module>r      sU    #   0 ) C C $  L LC0,$^Qh]@,Br+   