o
    ›¨ÊhI  ã                   @   s&  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m	Z	 d dl
mZ d dl
mZmZmZmZ d dlmZmZmZmZmZmZmZmZmZmZmZmZmZmZmZm Z m!Z! d dl"m#Z# d d	l$m%Z%m&Z&m'Z' G d
d„ de(ƒZ)G dd„ dej*e)d�Z+G dd„ de+ƒZ,efdd„Z-G dd„ de+ƒZ.dS )é    N)ÚImproperlyConfigured)Ú	force_str)Úconnection_routerÚconnections)Ú	Indexable)ÚDEFAULT_ALIASÚ	DJANGO_CTÚ	DJANGO_IDÚID)ÚBooleanFieldÚ	CharFieldÚ	DateFieldÚDateTimeFieldÚDecimalFieldÚEdgeNgramFieldÚFacetCharFieldÚFacetDateFieldÚFacetDateTimeFieldÚFacetIntegerFieldÚ
FloatFieldÚIntegerFieldÚLocationFieldÚMultiValueFieldÚ
NgramFieldÚSearchFieldÚSearchFieldError)ÚSearchIndexManager)Úget_facet_field_nameÚget_identifierÚget_model_ctc                       s   e Zd Z‡ fdd„Z‡  ZS )ÚDeclarativeMetaclassc              	      s€  i |d< z!dd„ |D ƒ}|  ¡  |D ]}t|dd ƒ}|r#|d  |¡ qW n	 ty.   Y nw i }| ¡ D ]\}}	t|	dƒrP|	j|vrHg ||	j< ||	j  |¡ q5i }
| ¡ D ]5\}}	t|	t	ƒrŒ|| }| 
|¡ ||
|< t|dƒsŒ|jrŒ||vrŒt|ƒ}|j|d�}| 
|¡ ||
|< qW|d  |
¡ d|vrµzt|d jƒ|d< W n ttfy´   ttƒ|d< Y nw tt| ƒ | |||¡S )NÚfieldsc                 S   s   g | ]	}t |tƒr|‘qS © )Ú
issubclassÚSearchIndex)Ú.0Úbr"   r"   úB/var/www/html/env/lib/python3.10/site-packages/haystack/indexes.pyÚ
<listcomp>(   s    z0DeclarativeMetaclass.__new__.<locals>.<listcomp>Ú	facet_for)r)   ÚobjectsÚMeta)ÚreverseÚgetattrÚupdateÚ	NameErrorÚitemsÚhasattrr)   ÚappendÚ
isinstancer   Úset_instance_nameÚfacetedr   Úfacet_classr   Úindex_labelÚKeyErrorÚAttributeErrorr   Úsuperr    Ú__new__)ÚclsÚnameÚbasesÚattrsÚparentsÚpr!   Úfacet_fieldsÚ
field_nameÚobjÚbuilt_fieldsÚfieldÚshadow_facet_nameÚshadow_facet_field©Ú	__class__r"   r'   r;   #   sT   €üÿ


€



€ÿzDeclarativeMetaclass.__new__)Ú__name__Ú
__module__Ú__qualname__r;   Ú__classcell__r"   r"   rI   r'   r    "   s    r    c                   @   s¼   e Zd ZdZdd„ Zdd„ Zd*dd„Zd*d	d
„Zd+dd„Zdd„ Z	d,dd„Z
dd„ Zdd„ Zdd„ Zd*dd„Zd*dd„Zd*dd„Zd*dd„Zd*d d!„Zd*d"d#„Zd$d%„ Zd&d'„ Zd(d)„ ZdS )-r$   aŒ  
    Base class for building indexes.

    An example might look like this::

        import datetime
        from haystack import indexes
        from myapp.models import Note

        class NoteIndex(indexes.SearchIndex, indexes.Indexable):
            text = indexes.CharField(document=True, use_template=True)
            author = indexes.CharField(model_attr='user')
            pub_date = indexes.DateTimeField(model_attr='pub_date')

            def get_model(self):
                return Note

            def index_queryset(self, using=None):
                return self.get_model().objects.filter(pub_date__lte=datetime.datetime.now())

    c                 C   sd   d | _ g }i | _| j ¡ D ]\}}|| j|j< |jdu r!| |¡ qt|ƒdks0td| j	j
 ƒ‚d S )NTé   úKThe index '%s' must have one (and only one) SearchField with document=True.)Úprepared_dataÚ	field_mapr!   r0   Úindex_fieldnameÚdocumentr2   Úlenr   rJ   rK   )ÚselfÚcontent_fieldsrC   rF   r"   r"   r'   Ú__init__u   s   

€ÿÿÿzSearchIndex.__init__c                 C   s   t d|  ƒ‚)zÓ
        Should return the ``Model`` class (not an instance) that the rest of the
        ``SearchIndex`` should use.

        This method is required & you must override it to return the correct class.
        z9You must provide a 'get_model' method for the '%r' index.)ÚNotImplementedError©rV   r"   r"   r'   Ú	get_model†   s   ÿzSearchIndex.get_modelNc                 C   ó   |   ¡ j ¡ S )z�
        Get the default QuerySet to index when doing a full update.

        Subclasses can override this method to avoid indexing certain objects.
        ©r[   Ú_default_managerÚall©rV   Úusingr"   r"   r'   Úindex_queryset‘   s   zSearchIndex.index_querysetc                 C   s   | j |d�S )zÒ
        Get the default QuerySet for read actions.

        Subclasses can override this method to work with other managers.
        Useful when working with default managers that filter some objects.
        ©ra   )rb   r`   r"   r"   r'   Úread_queryset™   s   zSearchIndex.read_querysetc           	      C   s¾   i }|   ¡ }|  ¡ }d|j }|r|r||d| < nt |¡ |r/|r*||d| < nt |¡ d}t| dƒr@t d¡ |  ¡ }n| j|d�}t|dƒsQtd	|  ƒ‚|j	d
i |¤Ž 
|jjj¡S )a7  
        Get the default QuerySet to index when doing an index update.

        Subclasses can override this method to take into account related
        model modification times.

        The default is to use ``SearchIndex.index_queryset`` and filter
        based on ``SearchIndex.get_updated_field``
        z>No updated date field found for '%s' - not restricting by age.z%s__gtez%s__lteNÚget_querysetzd'SearchIndex.get_queryset' was deprecated in Haystack v2. Please rename the method 'index_queryset'.rc   ÚfilterzGThe '%r' class must return a 'QuerySet' in the 'index_queryset' method.r"   )r[   Úget_updated_fieldrK   ÚwarningsÚwarnr1   re   rb   r   rf   Úorder_byÚ_metaÚpkr=   )	rV   ra   Ú
start_dateÚend_dateÚextra_lookup_kwargsÚmodelÚupdated_fieldÚupdate_field_msgÚindex_qsr"   r"   r'   Úbuild_queryset¢   s8   
þ


ÿ

ÿÿzSearchIndex.build_querysetc                 C   s|   t t|ƒtt|  ¡ ƒtt|jƒi| _| j	 
¡ D ]#\}}| |¡| j|j< t| d| ƒr:t| d| ƒ|ƒ}|| j|j< q| jS )z?
        Fetches and adds/alters data before indexing.
        z
prepare_%s)r
   r   r   r   r[   r	   r   rl   rQ   r!   r0   ÚpreparerS   r1   r-   )rV   rD   rC   rF   Úvaluer"   r"   r'   ru   Õ   s   
ý€zSearchIndex.prepareTc                 C   s°   |   |¡| _| j ¡ D ]I\}}|s |jdkr t|dd ƒ| jv r qt|dd ƒrB| j|j j}| j| d u rB|| jv rB| j| | j|j< |jdu rT| j|j d u rT| j|j= q| jS )NÚstringr)   T)	ru   rQ   r!   r0   Ú
field_typer-   r)   rS   Únull)rV   rD   Úwith_string_facetrC   rF   Úsource_field_namer"   r"   r'   Úfull_prepareê   s&   ÿ

ÿ

€zSearchIndex.full_preparec                 C   s,   | j  ¡ D ]\}}|jdu r|j  S qdS )zCReturns the field that supplies the primary document to be indexed.TN)r!   r0   rT   rS   )rV   Ú_rF   r"   r"   r'   Úget_content_field	  s
   

ÿÿzSearchIndex.get_content_fieldc                 C   s,   i }| j  ¡ D ]\}}|jr|j||< q|S )z+Returns a dict of fields with weight values)r!   r0   Úboost)rV   ÚweightsrC   rF   r"   r"   r'   Úget_field_weights  s   
€zSearchIndex.get_field_weightsc                 C   s   t  dt¡ |  |¡S )NzKSearchIndex._get_backend is deprecated; use SearchIndex.get_backend instead)rh   ri   ÚDeprecationWarningÚget_backendr`   r"   r"   r'   Ú_get_backend  s
   þ
zSearchIndex._get_backendc                 C   s>   |d u rz
t j| d�d }W n
 ty   Y d S w t|  ¡ S )N)Úindexr   )r   Ú	for_writeÚ
IndexErrorr   rƒ   r`   r"   r"   r'   rƒ     s   þzSearchIndex.get_backendc                 C   s.   |   |¡}|dur| | | j|d�¡ dS dS )zÒ
        Updates the entire index.

        If ``using`` is provided, it specifies which connection should be
        used. Default relies on the routers to decide which backend should
        be used.
        Nrc   )rƒ   r.   rb   ©rV   ra   Úbackendr"   r"   r'   r.   (  s   
	ÿzSearchIndex.updatec                 K   s>   | j |fi |¤Žr|  |¡}|dur| | |g¡ dS dS dS )a  
        Update the index for a single object. Attached to the class's
        post-save hook.

        If ``using`` is provided, it specifies which connection should be
        used. Default relies on the routers to decide which backend should
        be used.
        N)Úshould_updaterƒ   r.   ©rV   Úinstancera   Úkwargsr‰   r"   r"   r'   Úupdate_object6  s   

üzSearchIndex.update_objectc                 K   s,   |   |¡}|dur|j|fi |¤Ž dS dS )a  
        Remove an object from the index. Attached to the class's
        post-delete hook.

        If ``using`` is provided, it specifies which connection should be
        used. Default relies on the routers to decide which backend should
        be used.
        N)rƒ   Úremover‹   r"   r"   r'   Úremove_objectF  s   
	ÿzSearchIndex.remove_objectc                 C   s,   |   |¡}|dur|j|  ¡ gd� dS dS )zÑ
        Clears the entire index.

        If ``using`` is provided, it specifies which connection should be
        used. Default relies on the routers to decide which backend should
        be used.
        N)Úmodels)rƒ   Úclearr[   rˆ   r"   r"   r'   r’   T  s   
ÿzSearchIndex.clearc                 C   s   | j |d� | j|d� dS )zò
        Completely clear the index for this model and rebuild it.

        If ``using`` is provided, it specifies which connection should be
        used. Default relies on the routers to decide which backend should
        be used.
        rc   N)r’   r.   r`   r"   r"   r'   Úreindexa  s   zSearchIndex.reindexc                 C   ó   dS )ax  
        Get the field name that represents the updated date for the model.

        If specified, this is used by the reindex command to filter out results
        from the QuerySet, enabling you to reindex only recent records. This
        method should either return None (reindex everything always) or a
        string of the Model's DateField/DateTimeField name.
        Nr"   rZ   r"   r"   r'   rg   l  s   	zSearchIndex.get_updated_fieldc                 K   r”   )aN  
        Determine if an object should be updated in the index.

        It's useful to override this when an object may save frequently and
        cause excessive reindexing. You should check conditions on the instance
        and return False if it is not to be indexed.

        By default, returns True (always reindex).
        Tr"   )rV   rŒ   r�   r"   r"   r'   rŠ   w  s   
zSearchIndex.should_updatec                 C   r\   )ac  
        Provides the ability to override how objects get loaded in conjunction
        with ``SearchQuerySet.load_all``.

        This is useful for post-processing the results from the query, enabling
        things like adding ``select_related`` or filtering certain data.

        By default, returns ``all()`` on the model's default manager.
        r]   rZ   r"   r"   r'   Úload_all_querysetƒ  s   
zSearchIndex.load_all_queryset©N)NNN)T)rK   rL   rM   Ú__doc__rX   r[   rb   rd   rt   ru   r|   r~   r�   r„   rƒ   r.   rŽ   r�   r’   r“   rg   rŠ   r•   r"   r"   r"   r'   r$   ^   s*    


	3







r$   )Ú	metaclassc                   @   s   e Zd Zeddd�ZdS )ÚBasicSearchIndexT©rT   Úuse_templateN)rK   rL   rM   r   Útextr"   r"   r"   r'   r™   �  s    r™   c                 C   sh   |}|   ¡ dv rt}|S |   ¡ dv rt}|S |   ¡ dv r t}|S |   ¡ dv r*t}|S |   ¡ dv r2t}|S )zd
    Returns the Haystack field type that would likely be associated with each
    Django type.
    )r   r   )r   ÚNullBooleanField)ÚCommaSeparatedIntegerField)r   r   )r   ÚPositiveIntegerFieldÚPositiveSmallIntegerFieldÚSmallIntegerField)Úget_internal_typer   r   r   r   r   )ÚfÚdefaultÚresultr"   r"   r'   Úindex_field_from_django_field˜  s    òôö	ør¦   c                       s^   e Zd ZdZeddd�ZeeeddfZ	d‡ fdd„	Z
d	d
„ Zdd„ Zdd„ Zddd„Z‡  ZS )ÚModelSearchIndexa)  
    Introspects the model assigned to it and generates a `SearchIndex` based on
    the fields of that model.

    In addition, it adds a `text` field that is the `document=True` field and
    has `use_template=True` option set, just like the `BasicSearchIndex`.

    Usage of this class might result in inferior `SearchIndex` objects, which
    can directly affect your search results. Use this to establish basic
    functionality and move to custom `SearchIndex` objects for better control.

    At this time, it does not handle related fields.
    Trš   Úcontentrœ   Nc                    sÀ   t ƒ  ¡  d | _d | _g }|pi | _t| dd ƒ| _| jr<t| jdd ƒ| _t| jdg ƒ}t| jdg ƒ}| j |  	||¡¡ | j 
¡ D ]\}}|jdu rO| |¡ qAt|ƒdks^td| jj ƒ‚d S )Nr+   rp   r!   ÚexcludesTrO   rP   )r:   rX   rp   rQ   Úextra_field_kwargsr-   rk   r!   r.   Ú
get_fieldsr0   rT   r2   rU   r   rJ   rK   )rV   rª   rW   r!   r©   rC   rF   rI   r"   r'   rX   Å  s,   



€ÿÿÿzModelSearchIndex.__init__c                 C   s$   |j | jv rdS |js|jrdS dS )zv
        Given a Django model field, return if it should be included in the
        contributed SearchFields.
        TF)r=   Úfields_to_skipÚprimary_keyÚis_relation)rV   rF   r"   r"   r'   Úshould_skip_fieldå  s
   z"ModelSearchIndex.should_skip_fieldc                 C   s   | j S r–   )rp   rZ   r"   r"   r'   r[   ô  s   zModelSearchIndex.get_modelc                 C   s   |j S )zO
        Given a Django field, return the appropriate index fieldname.
        )r=   )rV   r£   r"   r"   r'   Úget_index_fieldname÷  s   z$ModelSearchIndex.get_index_fieldnamec                 C   sÔ   i }|pg }|p	g }| j jjD ]X}|j| jv rq|r |j|vr q|r(|j|v r(q|  |¡r.qt|ƒ}t | j¡}| d|ji¡ |j	du rId|d< | 
¡ rR|j|d< |di |¤Ž||j< ||j  |  |¡¡ q|S )zŠ
        Given any explicit fields to include and fields to exclude, add
        additional fields based on the associated model.
        Ú
model_attrTry   r¤   Nr"   )rp   rk   r!   r=   r¯   r¦   Úcopyrª   r.   ry   Úhas_defaultr¤   r4   r°   )rV   r!   r©   Úfinal_fieldsr£   Úindex_field_classr�   r"   r"   r'   r«   ý  s,   


zModelSearchIndex.get_fieldsr–   )NN)rK   rL   rM   r—   r   rœ   r
   r   r	   r¬   rX   r¯   r[   r°   r«   rN   r"   r"   rI   r'   r§   ²  s     r§   )/r²   Ú	threadingrh   Údjango.core.exceptionsr   Údjango.utils.encodingr   Úhaystackr   r   Úhaystack.constantsr   r   r   r	   r
   Úhaystack.fieldsr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   Úhaystack.managerr   Úhaystack.utilsr   r   r   Útyper    Úlocalr$   r™   r¦   r§   r"   r"   r"   r'   Ú<module>   s$    L<  4