
    h                     8   d Z ddlZddlZddlmZmZ ddlmZ ddlm	Z	m
Z
mZmZ ddlmZmZ ddlmZ ddlmZmZ dd	lmZ dd
lmZ ddlmZ 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%m&Z&m'Z' ddl(m)Z)m*Z*m+Z+m,Z,m-Z- ddl.m/Z/ ddl0m1Z1 ddl2m3Z3m4Z4m5Z5m6Z6m7Z7 dZ8 G d de9e      Z: ed e;      Z<d Z= G d d      Z> G d d      Z? G d de?ej                        ZA G d de      ZB G d  d!      ZC G d" d#eCej                  eB$      ZD G d% d&      ZE G d' d(      ZFy))af  
The models and fields for translation support.

The default is to use the :class:`TranslatedFields` class in the model, like:

.. code-block:: python

    from django.db import models
    from parler.models import TranslatableModel, TranslatedFields


    class MyModel(TranslatableModel):
        translations = TranslatedFields(
            title = models.CharField(_("Title"), max_length=200)
        )

        class Meta:
            verbose_name = _("MyModel")

        def __str__(self):
            return self.title


It's also possible to create the translated fields model manually:

.. code-block:: python

    from django.db import models
    from parler.models import TranslatableModel, TranslatedFieldsModel
    from parler.fields import TranslatedField


    class MyModel(TranslatableModel):
        title = TranslatedField()  # Optional, explicitly mention the field

        class Meta:
            verbose_name = _("MyModel")

        def __str__(self):
            return self.title


    class MyModelTranslation(TranslatedFieldsModel):
        master = models.ForeignKey(MyModel, related_name='translations', null=True)
        title = models.CharField(_("Title"), max_length=200)

        class Meta:
            verbose_name = _("MyModel translation")

This has the same effect, but also allows to to override
the :func:`~django.db.models.Model.save` method, or add new methods yourself.

The translated model is compatible with django-hvad, making the transition between both projects relatively easy.
The manager and queryset objects of django-parler can work together with django-mptt and django-polymorphic.
    N)OrderedDictdefaultdict)settings)
FieldErrorImproperlyConfiguredObjectDoesNotExistValidationError)modelsrouter)	ModelBase)ForwardManyToOneDescriptorManyToManyDescriptor)	force_str)lazy)gettext)gettext_lazy)signals)MISSING_cache_translation!_cache_translation_needs_fallback_delete_cached_translation_delete_cached_translationsget_cached_translated_fieldget_cached_translation
is_missing)LanguageCodeDescriptorTranslatedFieldTranslatedFieldDescriptorTranslationsForeignKey_validate_master)TranslatableManager)compat)get_languageget_language_settingsget_language_titleget_null_language_errornormalize_language_code)TranslatableModelMixinTranslatableModelTranslatedFieldsTranslatedFieldsModelTranslatedFieldsModelBaseTranslationDoesNotExistc                       e Zd ZdZy)r-   a'  
    A tagging interface to detect missing translations.
    The exception inherits from :class:`~exceptions.AttributeError` to reflect what is actually happening.
    Therefore it also causes the templates to handle the missing attributes silently, which is very useful in the admin for example.
    The exception also inherits from :class:`~django.core.exceptions.ObjectDoesNotExist`,
    so any code that checks for this can deal with missing translations out of the box.

    This class is also used in the ``DoesNotExist`` object on the translated model, which inherits from:

    * this class
    * the ``sharedmodel.DoesNotExist`` class
    * the original ``translatedmodel.DoesNotExist`` class.

    This makes sure that the regular code flow is decently handled by existing exception handlers.
    N)__name__
__module____qualname____doc__     =/home/dcms/DCMS/lib/python3.12/site-packages/parler/models.pyr-   r-   u   s      	r4   r-   c                 ^    t        d      j                  | j                  j                        S )Nz{0} Translation)r   format_metaverbose_name)xs    r5   <lambda>r;      s!    G,=$>$E$EaggFZFZ$[ r4   c                 v   |si }| j                   j                  rt        d| j                         | j                   j                  |d<   | j                   j
                  |d<   | j                   j                  |d<   t        |j                  dg             dgz   |d<   |j                  d| j                   j                   d       |j                  d	t        |              |j                  d
d       t        | j                   d      }i }|j                  |       t        dt        f|      |d<   | j                   |d<   t#        j$                         |d<   t'        | |ddt"        j(                        |d<   t+        |t,        f|      }t.        j0                  | j                      }t3        |||       |S )aP  
    Dynamically create the translations model.
    Create the translations model for the shared model 'model'.

    :param related_name: The related name for the reverse FK from the translations model.
    :param meta: A (optional) dictionary of attributes for the translations model's inner Meta class.
    :param fields: A dictionary of fields to put on the translations model.

    Two fields are enforced on the translations model:

        language_code: A 15 char, db indexed field.
        master: A ForeignKey back to the shared model.

    Those two fields are unique together.
    z6Can't create TranslatedFieldsModel for abstract class 	app_labeldb_tablespacemanagedunique_together)language_codemasterdb_table_translationr9   default_permissionsr3   TranslationMetar0   objectsFT)related_nameeditablenull	on_deleterB   )r8   abstract	TypeErrorr/   r=   r>   r?   listget
setdefaultrC   _lazy_verbose_namestrupdatetypeobjectr0   r
   Managerr   CASCADEr,   r+   sysmodulessetattr)shared_modelrI   metafieldsnameattrstranslations_modelmods           r5   create_translations_modelrc      s     ""D\EZEZD[\
 	

 %**44D(..<<D"((00DO"488,=r#BCGbFccD	OOJ<#5#5#>#>"?| LMOON$6|$DE 	OO)2. ,''(45DE	LL&D1E&M&11E,~~'E),!..E(O 34:O9QSXY
 ++l--
.CC)*r4   c                       e Zd ZdZddZd Zy)r*   a  
    Wrapper class to define translated fields on a model.

    The field name becomes the related name of the :class:`TranslatedFieldsModel` subclass.

    Example:

    .. code-block:: python

        from django.db import models
        from parler.models import TranslatableModel, TranslatedFields

        class MyModel(TranslatableModel):
            translations = TranslatedFields(
                title = models.CharField("Title", max_length=200)
            )

    When the class is initialized, the attribute will point
    to a :class:`~django.db.models.fields.related.ForeignRelatedObjectsDescriptor` object.
    Hence, accessing ``MyModel.translations.related.related_model`` returns the original model
    via the :class:`django.db.models.related.RelatedObject` class.

    ..
       To fetch the attribute, you can also query the Parler metadata:
       MyModel._parler_meta.get_model_by_related_name('translations')

    :param meta: A dictionary of `Meta` options, passed to the :class:`TranslatedFieldsModel`
        instance.

        Example:

        .. code-block:: python

            class MyModel(TranslatableModel):
                translations = TranslatedFields(
                    title = models.CharField("Title", max_length=200),
                    slug = models.SlugField("Slug"),
                    meta = {'unique_together': [('language_code', 'slug')]},
                )

    Nc                 .    || _         || _        d | _        y N)r^   r]   r_   )selfr]   r^   s      r5   __init__zTranslatedFields.__init__   s    		r4   c                 V    || _         t        ||| j                  fi | j                   y rf   )r_   rc   r]   r^   )rg   clsr_   kwargss       r5   contribute_to_classz$TranslatedFields.contribute_to_class   s"    	!#tTYYF$++Fr4   rf   )r/   r0   r1   r2   rh   rl   r3   r4   r5   r*   r*      s    (T
Gr4   r*   c                        e Zd ZdZdZ e       Z fdZddZd Z	ddZ
d ZddZd	 Zd
 ZddZddZddZ	 ddZddZddZddZddZ fdZd fd	Zd fd	Zd Zd Zd dZ fdZde_         xZS )!r(   z
    Base model mixin class to handle translations.

    All translatable fields will appear on this model, proxying the calls to the :class:`TranslatedFieldsModel`.
    Nc                    i }d }|rF|j                  dd       }| j                  j                         D ]  }	 |j                  |      ||<    d | _        d | _        t        |   |i | t        t              | _        t        |xs
 t                     | _        |r | j                  | j
                  fi | y y # t        $ r Y w xY w)N_current_language)pop_parler_metaget_all_fieldsKeyError_translations_cachero   superrh   r   dictr'   r#   _set_translated_fields)rg   argsrk   translated_kwargscurrent_languagefield	__class__s         r5   rh   zTranslatableModelMixin.__init__  s    %zz*=tD**99; /5zz%/@%e, $( !% 	$)&) $/t#4 !8."
 'D''(>(>TBST %   s   B<<	CCc                 D   g } | j                   j                  di |D ]P  \  }}| j                  |d|      }|j                         D ]  \  }}	 t	        |||        |j                  |       R |S # t
        $ r" t        |di       }	||	|<   t	        |d|	       Y Uw xY w)z9
        Assign fields to the translated models.
        T)rA   auto_creater]   deferred_many_to_manyr3   )rq   _split_fields_get_translated_modelitemsr[   rN   getattrappend)
rg   rA   r^   rH   parler_metamodel_fieldstranslationr{   valuer   s
             r5   rw   z-TranslatableModelMixin._set_translated_fields/  s     )H):):)H)H)R6)R 	(%K44+K 5 K !- 2 2 4 YuYK6Y NN;'	(  ! Y -4KAXZ\,])38)%0K)@BWXYs   A44(BBc                    |t        t                     | j                  }| j                  |j                     j                  |d      rt        d|        | j                  |fi |D ]  }| j                  |        y)aO  
        Add a translation to the model.

        The :func:`save_translations` function is called afterwards.

        The object will be saved immediately, similar to
        calling :func:`~django.db.models.manager.Manager.create`
        or :func:`~django.db.models.fields.related.RelatedManager.create` on related fields.
        NzTranslation already exists: )
ValueErrorr&   rq   rt   
root_modelrP   rw   save_translation)rg   rA   r^   r]   r   s        r5   create_translationz)TranslatableModelMixin.create_translationE  s      4677  ##DOO4884
 ;M?KLL 7466}OO 	/K!!+.	/r4   c                    |t        t                     || j                  }n| j                  |   g}d}|D ]^  }	 | j                  ||      }|j                          |dz  }	 | j                  |j                     |= 	 | j                  |j                  = ` |st        d|       |S # |j                  j
                  $ r Y w xY w# t        $ r Y Vw xY w# t        t        f$ r Y w xY w)z
        Delete a translation from a model.

        :param language_code: The language to remove.
        :param related_name: If given, only the model matching that related_name is removed.
        r   r]      zTranslation does not exist: )r   r&   rq   r   modelDoesNotExistdeletert   rs   _prefetched_objects_cacherel_nameAttributeError)rg   rA   rI   metasnum_deletedr]   r   s          r5   delete_translationz)TranslatableModelMixin.delete_translation\  s     4677%%E&&|45E 	D"88T8R  1K,,TZZ8G224==A#	* ;M?KLL+ ::**    #H- s5   B,&CC,CC	CCC,+C,c                     | j                   S )z+
        Get the current language.
        )ro   rg   s    r5   get_current_languagez+TranslatableModelMixin.get_current_language  s    
 %%%r4   c                 h    t        |xs
 t                     | _        |r| j                  dd       yy)zG
        Switch the currently activate language of the object.
        FT)use_fallbackr~   N)r'   r#   ro   r   )rg   rA   
initializes      r5   set_current_languagez+TranslatableModelMixin.set_current_language  s5     "99X,.!Y &&Et&L r4   c                 4    | j                         }|r|d   S dS )z\
        .. deprecated:: 1.5
           Use :func:`get_fallback_languages` instead.
        r   N)get_fallback_languages)rg   	fallbackss     r5   get_fallback_languagez,TranslatableModelMixin.get_fallback_language  s#    
 //1	(y|2d2r4   c                     t        | j                        }|d   D cg c]  }|| j                  k7  s| }}|xs g S c c}w )z
        Return the fallback language codes,
        which are used in case there is no translation for the currently active language.
        r   )r$   ro   )rg   	lang_dictlangr   s       r5   r   z-TranslatableModelMixin.get_fallback_languages  sI    
 *$*@*@A	&/&<_dH^H^@^T_	_B `s   >>c                    |!| j                   }|t        t                     | j                  j	                  |      }	 t        | j                  |j                     |          S # t        $ rq || j                  |      v rY yt        | ||d      }||j                  |k(  cY S 	 | j                  |dd|       Y y# |j                  j                  $ r Y Y yw xY ww xY w)z
        Return whether a translation for the given language exists.
        Defaults to the current language code.

        .. versionadded 1.2 Added the ``related_name`` parameter.
        r   TrI   r   Fr   r~   r]   )ro   r   r&   rq   _get_extension_by_related_namer   rt   r   rs   _read_prefetched_translationsr   rA   r   r   )rg   rA   rI   r]   rV   s        r5   has_translationz&TranslatableModelMixin.has_translation  s      22M$ !8!:;;  ??M	 "$":":4::"F}"UVVV 	
  B B B MM ,m,TF !++}<<**!5t +   ::** +	s0    %A& &C  C (B??CC CC c                    | j                   j                  |      }| j                  |      }|t        d |D              }n4| j	                  |      }|j                  dd      j                  d      }|rNd | j                  |j                     j                         D        }t        t        |      t        |      z        S |S )z
        Return the language codes of all translated variations.

        .. versionadded 1.2 Added the ``include_unsaved`` and ``related_name`` parameters.
        r   c              3   4   K   | ]  }|j                     y wrf   rA   ).0objs     r5   	<genexpr>zATranslatableModelMixin.get_available_languages.<locals>.<genexpr>  s     !H#"3"3!Hs   rA   T)flatc              3   >   K   | ]  \  }}t        |      r|  y wrf   r   )r   kvs      r5   r   zATranslatableModelMixin.get_available_languages.<locals>.<genexpr>  s"      aR\]^R_s   )rq   r   _get_prefetched_translationssorted_get_translated_querysetvalues_listorder_byrt   r   r   rO   set)rg   rI   include_unsavedr]   prefetchdb_languagesqslocal_languagess           r5   get_available_languagesz.TranslatableModelMixin.get_available_languages  s       ??M44$4?!!Hx!HHL..D.9B>>/>ENN_L"66tzzBHHJO L)C,@@AAr4   c                 ^    | j                   j                  |      }| j                  ||      S )z,
        Fetch the translated model
        r   )rq   r   r   )rg   rA   rI   r]   s       r5   get_translationz&TranslatableModelMixin.get_translation  s0       ??M))-d)CCr4   c           	      X   | j                   t        d      | j                  t        d      |s!| j                  }|t        t                     || j                   j                  }| j                  |j                     }	 ||   }t        |      s|S 	 |rDd|i}| j                  r| j                  j                  s| |d<    |j                  di |}|||<   |S d}	t/        |      }
||vrCt&        ||<   | j                  j                  r| j                  t1        | ||j$                  	       |
d
   gt3        |
d         z   }|rI|rG|D ]  }||k(  r		 | j5                  |d||      c S  dj7                  dj9                  |
d               }	|j                  j-                  dj7                  | j:                  j<                  | j                  ||	xs d            # t        $ r | j                  j                  s| j                  | j                  |      }|-|D ]'  }|j                  |k(  s|||<   t!        |       |c cY S  nt#        | ||j$                  |      }|+|j                  |k7  r	t&        ||<   |||j                  <   |cY S t        |j)                  |d            rnU	 | j+                  |      j)                  |      }|||<   t!        |       |cY S # |j                  j,                  $ r Y nw xY wY qw xY w# |j                  j,                  $ r Y w xY w)z4
        Fetch the translated fields model.
        Nz0No translation is assigned to the current model!zDAccessing translated fields before super.__init__() is not possible.r   r   r   rA   rB   rI   coder   Fr   z (tried fallbacks {}), zV{0} does not have a translation for the current language!
{0} ID #{1}, language={2}{3} r3   )rq   r   rt   RuntimeErrorro   r   r&   rootr   r   rs   _stateaddingpkr   rA   r   r   r   r   rP   r   r   r$   r   rO   r   r7   joinr8   r9   )rg   rA   r   r~   r]   local_cacherV   r   rk   fallback_msgr   fallback_choicesfallback_langs                r5   r   z,TranslatableModelMixin._get_translated_model  sx    $&'YZZ##+V   22M$ !8!:;;<$$))D..tzz:.	* /F f% &`   F wwt{{11#'x TZZ)&)F)/K&M )-8	+ *1K&;;%%)<1$TXTaTab%f-.i6L1MM, "2 !]255%E{Y] 6   399$))IkDZ:[\L jj%%++16

''-ASQS,
 	
w  (	* ;;%%$''*=<<$<G' #+ *!//=@9?K6.v6#)M	* 4m$--VbF )!//=@9@K6<BF$8$89%#KOOM4$HI 	*%)%B%B4%H%L%L.; &M &F :@K6.v6#)M  $zz66 ! !G(	*j zz.. s\   G  L AL	6L	AL	L	1!K&L	&L?L	LL	L	L)(L)c                    || j                   j                  }|j                  }| j                  |   }|rj| j                  g| j                         z   }	 |D ]&  }|j                  |d      }|st        |      r$|c S  t        d |j                         D              S 	 | j                  |      }||d   }n| j                  |      d   }|||j                  <   t        |       |S # t        $ r Y Ww xY w# t        $ r Y yw xY w)zm
        Return any available translation.
        Returns None if there are no translations at all.
        Nc              3   8   K   | ]  }t        |      r|  y wrf   r   )r   ts     r5   r   zCTranslatableModelMixin._get_any_translated_model.<locals>.<genexpr>  s     Q!:a=AQs   r   r   )rq   r   r   rt   ro   r   rP   r   nextvaluesStopIterationr   r   rA   r   
IndexError)	rg   r]   tr_modelr   check_languagesr   transr   r   s	            r5   _get_any_translated_modelz0TranslatableModelMixin._get_any_translated_models  s*   
 <$$))D::..x8  $5569T9T9VVO%4 %M'OOM4@EZ%6$% Q{'9'9';QQQ	88d8CH#&qk";;;FqI 6AK112{+ !   		s6   C, /C, ;C, ? C, !/C; ,	C87C8;	DDc                 ~    || j                   j                  }t        | |j                        }|j	                         S )z
        Return the queryset that points to the translated model.
        If there is a prefetch, it can be read from this queryset.
        )rq   r   r   r   get_queryset)rg   r]   accessors      r5   r   z/TranslatableModelMixin._get_translated_queryset  s9     <$$))D4/$$&&r4   c                     || j                   j                  }|j                  }	 | j                  |   S # t        t
        f$ r Y yw xY w)z<
        Return the queryset with prefetch results.
        N)rq   r   r   r   r   rs   )rg   r]   rI   s      r5   r   z3TranslatableModelMixin._get_prefetched_translations  sR     <$$))D}}	 11,??) 		s   5 AAc                    || j                   j                  }| j                  |j                     }| j	                  |      }g }|<|D ]7  }|j
                  }|j                  |       ||vst        ||         s3|||<   9 |S )Nr   )rq   r   rt   r   r   rA   r   r   )rg   r]   r   r   languages_seenr   r   s          r5   r   z4TranslatableModelMixin._read_prefetched_translations  s    <$$))D..tzz:44$4?' 4"00%%d+{*jT9J.K(3K%	4 r4   c                 l    t        |   |i | |j                  dd         | j                  |i | y )Nupdate_fields)ru   saverp   save_translationsrg   rx   rk   r|   s      r5   r   zTranslatableModelMixin.save  s9    d%f%
 	

?D)//r4   c                 8    t        |        t        | 	  |      S rf   )r   ru   r   rg   usingr|   s     r5   r   zTranslatableModelMixin.delete  s    #D)w~e$$r4   c                    i }	 t         |   |       | j                  j                         D ]6  }|j                         D ]!  }t        |      r	 |j                  |       # 8 |rt        |      y# t        $ r}|j                  }Y d}~{d}~ww xY w# t        $ r%}|j                  |j                         Y d}~d}~ww xY w)zL
        Also validate the unique_together of the translated model.
        )excludeN)ru   validate_uniquer	   
error_dictrt   r   r   rT   )rg   r   errorser   r   r|   s         r5   r   z&TranslatableModelMixin.validate_unique  s    
 	"G#G#4  33::< 	0K*113 0k*0///@0	0 !&))   	"\\F	" ' 0MM!,,//0s/   A6 B6	B?BB	C!CCc                     | j                   j                         }| j                  D ]S  }||j                     }t	        |j                               }|D ]$  }t        |      r | j                  |g|i | & U y)aQ  
        The method to save all translations.
        This can be overwritten to implement any custom additions.
        This method calls :func:`save_translation` for every fetched language.

        :param args: Any custom arguments to pass to :func:`save`.
        :param kwargs: Any custom arguments to pass to :func:`save`.
        N)rt   copyrq   r   rO   r   r   r   )rg   rx   rk   local_cachesr]   r   translationsr   s           r5   r   z(TranslatableModelMixin.save_translations  s     //446%% 
	DD&tzz2K 2 2 45L  , Dk*%%%kCDCFC	D
	Dr4   c                    | j                   | j                  j                  rt        d      |j                   |j                  rJ|j
                  s,| j                  j                  |j                  _        | |_         |j                  |i | t        |di       }|rD|j                         D ]   \  }}t        ||      j                  |       " |j                          yy)a+  
        Save the translation when it's modified, or unsaved.

        .. note::

           When a derived model provides additional translated fields,
           this method receives both the original and extended translation.
           To distinguish between both objects, check for ``translation.related_name``.

        :param translation: The translation
        :type translation: TranslatedFieldsModel
        :param args: Any custom arguments to pass to :func:`save`.
        :param kwargs: Any custom arguments to pass to :func:`save`.
        Nz@Can't save translations when the master object is not yet saved.r   )r   r   r   r   is_modified	master_iddbrB   r   r   r   r   )rg   r   rx   rk   r   	fieldnamer   s          r5   r   z'TranslatableModelMixin.save_translation  s     77?dkk00abb
 >>![%<%<(((,""%%)"Kd-f- !(5Lb Q $9$?$?$A ; 	5Y/33E:; !r4   c                 ~   | j                   j                  |      }|r0|| j                  k7  r!	 | j                  ||d      }t	        ||      S 	 t	        | |      S # t
        $ r Y nw xY w# t
        $ r Y nw xY w|r0| j                  |      }|	 t	        ||      S # t        $ r Y nw xY wt        |      r |       S |S )a{  
        Fetch a translated property, and return a default value
        when both the translation and fallback language are missing.

        When ``any_language=True`` is used, the function also looks
        into other languages to find a suitable value. This feature can be useful
        for "title" attributes for example, to make sure there is at least something being displayed.
        Also consider using ``field = TranslatedField(any_language=True)`` in the model itself,
        to make this behavior the default for the given field.

        .. versionchanged 1.5:: The *default* parameter may also be a callable.
        T)r]   r   r   )	rq   _get_extension_by_fieldro   r   r   r-   r   rs   callable)rg   r{   defaultrA   any_languager]   r   r   s           r5   safe_translation_getterz.TranslatableModelMixin.safe_translation_getter%  s       88? ]d.D.DD55m$]a5bx//tU++ +  +  88d8CK&";66  G9Ns5   A A* 	A'&A'*	A65A6B 	B('B(c                 n    t        |   |i | t        |        | j                  j	                          y rf   )ru   refresh_from_dbr   rt   clearr   s      r5   r  z&TranslatableModelMixin.refresh_from_dbP  s/    00#D)  &&(r4   Trf   )F)NN)NF)NFFN)NNF) r/   r0   r1   r2   rq   r   rA   rh   rw   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  alters_data__classcell__r|   s   @r5   r(   r(     s     L +,MU<,/.(T&M3*X 0D OS~
@#J
'$0%*0D0 D)V)
 #'Or4   r(   c                   2    e Zd ZdZ G d d      Z e       Zy)r)   z
    Base model class to handle translations.

    All translatable fields will appear on this model, proxying the calls to the :class:`TranslatedFieldsModel`.
    c                       e Zd ZdZy)TranslatableModel.MetaTN)r/   r0   r1   rM   r3   r4   r5   rG   r  _  s    r4   rG   N)r/   r0   r1   r2   rG   r!   rH   r3   r4   r5   r)   r)   X  s      "#Gr4   r)   c                   "     e Zd ZdZ fdZ xZS )r,   a7  
    .. versionadded 1.2

    Meta-class for the translated fields model.

    It performs the following steps:

    * It validates the 'master' field, in case it's added manually.
    * It tells the original model to use this model for translations.
    * It adds the proxy attributes to the shared model.
    c                    t         |   | |||      }|d   t        j                  k(  r|S |j                  j
                  s|j                  j                  r|S t        t        |j                  d      t              sYt        j                  dj                  |j                  j                        t               t!        |      }|j#                  |       |S )Nr   r{   zsPlease change {}.master to a parler.fields.TranslationsForeignKey field to support translations in data migrations.)ru   __new__r
   Modelr8   rM   proxy
isinstancer   rB   r   warningswarnr7   
model_nameDeprecationWarningr    contribute_translations)mcsr_   basesr`   	new_classr\   r|   s         r5   r  z!TranslatedFieldsModelBase.__new__s  s    GOCue<	8v||# ??##y'<'<')"2"2G<>TUMM##)6)//*D*D#E" ,I6L --l;r4   )r/   r0   r1   r2   r  r  r	  s   @r5   r,   r,   f  s    
 r4   r,   c                        e Zd ZdZdZ fdZed        Zed        Zed        Z	ed        Z
d fd	Zd fd		Zd
 Zd Zedd       Zed        Zd Zd Z xZS )TranslatedFieldsModelMixinzD
    Base class for the model that holds the translated fields.
    Nc                     t         j                  j                  | j                  ||       t	        |   |i | | j                         | _        t         j                  j                  | j                  ||       y )N)senderrx   rk   )	r   pre_translation_initsendr|   ru   rh   _get_field_values_original_valuespost_translation_initr   s      r5   rh   z#TranslatedFieldsModelMixin.__init__  sf    $$))dSY)Z$)&) $ 6 6 8%%**$..tTZ*[r4   c                 <    | j                   | j                         k7  S )zP
        Tell whether the object content is modified since fetching it.
        )r"  r!  r   s    r5   r   z&TranslatedFieldsModelMixin.is_modified  s    
 $$(>(>(@@@r4   c                 :    t        | j                               dk(  S )z;
        True when there are no translated fields.
        r   )lenget_translated_fieldsr   s    r5   is_emptyz#TranslatedFieldsModelMixin.is_empty  s    
 4--/0A55r4   c                 j    | j                   j                  j                  j                  j                  S )zC
        Returns the shared model this model is linked to.
        )r|   rB   r{   remote_fieldr   r   s    r5   r\   z'TranslatedFieldsModelMixin.shared_model  s&    
 ~~$$**77===r4   c                 j    | j                   j                  j                  j                  j                  S )z[
        Returns the related name that this model is known at in the shared model.
        )r|   rB   r{   r*  rI   r   s    r5   rI   z'TranslatedFieldsModelMixin.related_name  s&    
 ~~$$**77DDDr4   c                    | j                   J d       |xs! t        j                  | j                  |       }| j                  d u}| j
                  j                  s-t        j                  j                  | j                  | ||       t        | 0  d||d| | j                         | _        t        |        | j
                  j                  s0t        j                   j                  | j                  | | ||       y y )NzgNo language is set or detected for this TranslatableModelMixin.
Is the translations system initialized?instance)r  r.  rawr   )r/  r   )r  r.  createdr/  r   r3   )rA   r   db_for_writer|   r   r8   auto_createdr   pre_translation_saver   r\   ru   	save_baser!  r"  r   post_translation_save)rg   r/  r   rk   record_existsr|   s        r5   r4  z$TranslatedFieldsModelMixin.save_base  s     !!- 	
6	
- K,,T^^dKt+zz&&((--((4S . 
 	9c9&9 $ 6 6 84  zz&&))..((** /  'r4   c                    |xs! t        j                  | j                  |       }| j                  j                  s,t
        j                  j                  | j                  | |       t        | )  |       t        |        | j                  j                  s-t
        j                  j                  | j                  | |       y y )Nr-  )r  r.  r   )r   )r   r1  r|   r8   r2  r   pre_translation_deleter   r\   ru   r   r   post_translation_deleter   s     r5   r   z!TranslatedFieldsModelMixin.delete  s    K,,T^^dKzz&&**//((4u 0  	U#"4( zz&&++00((4u 1  'r4   c                     | j                   j                         D cg c]*  }|j                  r|j                  r|j	                         , c}S c c}w rf   )r8   
get_fieldsis_relationmany_to_oneget_attnamerg   r{   s     r5   _get_field_namesz+TranslatedFieldsModelMixin._get_field_names  sK     ..0
$$(9(9 
 	
 
s   /Ac                     | j                   j                         D cg c]4  }|j                  r|j                  rt	        | |j                               6 c}S c c}w rf   )r8   r;  r<  r=  r   r>  r?  s     r5   r!  z,TranslatedFieldsModelMixin._get_field_values  sR     ..0
$$(9(9 D%++-.
 	
 
s   9Ac                    | j                   j                  D cg c]  }|j                  dvr|j                   }}|r?|| j                   j                  D cg c]  }|j                  dvr|j                   c}z  }|S c c}w c c}w )N)rA   rB   id)r8   local_fieldsr_   local_many_to_many)rj   include_m2mfress       r5   r'  z0TranslatedFieldsModelMixin.get_translated_fields  s     YY++
vv>> FF
 

 5566!BB  C
 

s   !A?!Bc                 |   	 |j                   }|W|d   j                  |u rF|j	                  t        || | j                  j                  j                  j                               n<t        ||| | j                  j                  j                  j                        |_         | j                         D ]  }	 t        ||      }t        |t        j                  t         f      st        d|j"                   d| d      |j                  j$                  |uset'        |j                  j(                  	      j+                  ||        t-        d
t.        |j0                  | j0                  fi       | _        y# t        $ r t        d| d      w xY w# t        $ r t'               j+                  ||       Y w xY w)z?
        Add the proxy attributes to the shared model.
        zTranslatable model z2 does not appear to inherit from TranslatableModelN)r\   ra   rI   zThe model 'z' already has a field named '')r  r   )rq   r   rN   r\   add_meta
ParlerMetarB   r{   r*  rI   ParlerOptionsr'  r   r  r
   Fieldr   r/   r   r   r  rl   rU   r-   r   )rj   r\   baser_   shared_fields        r5   r  z2TranslatedFieldsModelMixin.contribute_translations  s   	,,D R 5 5 EMM!-'*!$!1!1!>!>!K!K )6)#& ZZ--::GG	)L% --/ 	>D>  '|T: ",?X0YZ#%l&;&;%<<YZ^Y__`a   %%++<?#%1%7%7%D%D)),=3	>:  '))  
 
m  	%l^3ef 	B " J!55lDIJs   E8 7F8F#F;:F;c                 >    t        t        | j                              S rf   )r   r%   rA   r   s    r5   __str__z"TranslatedFieldsModelMixin.__str__U  s    +D,>,>?@@r4   c                     dj                  | j                  j                  | j                  | j                  | j
                        S )Nz<{}: #{}, {}, master: #{}>)r7   r|   r/   r   rA   r   r   s    r5   __repr__z#TranslatedFieldsModelMixin.__repr__X  s6    +22NN##TWWd.@.@$..
 	
r4   )FNrf   T)r/   r0   r1   r2   rB   rh   propertyr   r(  r\   rI   r4  r   r@  r!  classmethodr'  r  rS  rU  r  r	  s   @r5   r  r    s    
 F\ A A 6 6 > > E E"H"

   F
 F
PA
r4   r  c                   l    e Zd Z ej                   ed      ej                  dd      Z G d d      Z	y)r+   Language   T)choices
max_lengthdb_indexc                       e Zd ZdZdZy)TranslatedFieldsModel.MetaTr3   N)r/   r0   r1   rM   rE   r3   r4   r5   rG   r`  e  s     r4   rG   N)
r/   r0   r1   r"   HideChoicesCharField_r   	LANGUAGESrA   rG   r3   r4   r5   r+   r+   ^  s3     0F//	*x11b4M! !r4   r+   )	metaclassc                   $    e Zd ZdZd ZddZd Zy)rM  z3
    Meta data for a single inheritance level.
    c                 .    || _         || _        || _        y rf   )r\   r   r   )rg   r\   ra   rI   s       r5   rh   zParlerMeta.__init__o  s    ('
$r4   c                 :    | j                   j                  |      S )z=
        Return the translated fields of this model.
        rF  )r   r'  )rg   rF  s     r5   r'  z ParlerMeta.get_translated_fieldsu  s     zz//K/HHr4   c                     dj                  | j                  j                  | j                  | j                  j                        S )Nz<ParlerMeta: {}.{} to {}>)r7   r\   r/   r   r   r   s    r5   rU  zParlerMeta.__repr__}  s6    *11&&tzz7J7J
 	
r4   NrV  )r/   r0   r1   r2   rh   r'  rU  r3   r4   r5   rM  rM  j  s    %I
r4   rM  c                       e Zd ZdZd Zd Zd Zed        Zd Z	d Z
d Zd	 Zd
 Zd ZddZd Zd Zd Zd Zd Zd Zd Zy)rN  z0
    Meta data for the translatable models.
    c                    |d cxu rt        |t              urt        d       || _        d| _        |,|| _        || _        d | _        g | _        t               | _
        ny|j                  xs |}d|_        || _        |j
                  | _        |j                  | _        t        |j                        | _        |j                  j                         | _
        | j                  t        |||             y )Nz Expected a TranslatedFieldsModelFT)
issubclassr+   rN   rP  	inheritedr   root_rel_name_root_extensionsr   _fields_to_modelrO   r   rL  rM  )rg   rP  r\   ra   rI   r   s         r5   rh   zParlerOptions.__init__  s    bZ8JLa-bb>?? c 	<0DO!-D DJ!D$/MD!
 ::%D!DNDJ"ooDO!%!3!3D  $D$4$45D$($9$9$>$>$@D!j/A<PQr4   c                     | j                   rt        d      | j                  j                  |       |j                  }|j                         D ]  }|| j                  |<    y )NzRAdding translations afterwards to an already inherited model is not supported yet.)rm  r   rp  r   r   r'  rq  )rg   r]   ra   r_   s       r5   rL  zParlerOptions.add_meta  sc    >>d  	% "ZZ&<<> 	=D*<D!!$'	=r4   c           	         | j                   }dj                  |j                  j                  |j                  |j
                  j                  t        | j                        dk(  rd      S dt        | j                         d      S )Nz<ParlerOptions: {}.{} to {}{}>r   r   r   z extensions)r   r7   r\   r/   r   r   r&  rp  )rg   r   s     r5   rU  zParlerOptions.__repr__  sz    yy/66&&MMJJd&&'1,B	
 	
 57s4;K;K7L6M[2Y	
 	
r4   c                      | j                   d   S )z
        The top level object in the inheritance chain.
        This is an alias for accessing the first item in the collection.
        r   )rp  r   s    r5   r   zParlerOptions.root  s     ""r4   c                 ,    t        | j                        S )zD
        Access all :class:`ParlerMeta` objects associated.
        )iterrp  r   s    r5   __iter__zParlerOptions.__iter__  s     D$$%%r4   c                    	 t        t              r| j                     S t        t              r| j	                        S t        fd| j                  D              S # t        t        t        f$ r t        d d      w xY w)zF
        Get an :class:`ParlerMeta` object by index or model.
        r   c              3   B   K   | ]  }|j                   k(  s|  y wrf   r   )r   r]   items     r5   r   z,ParlerOptions.__getitem__.<locals>.<genexpr>  s     TTtASDTs   zItem 'z' not found)	r  intrp  rS   r   r   r   r   rs   )rg   r{  s    `r5   __getitem__zParlerOptions.__getitem__  s    	7$$''--D#&:::MMTT-=-=TTTz84 	7VD6566	7s   A" !A" A" "$Bc                 ,    t        | j                        S rf   )r&  rp  r   s    r5   __len__zParlerOptions.__len__  s    4##$$r4   c                 T    | j                   D cg c]  }|j                   c}S c c}w )zT
        Return all translated models associated with the the shared model.
        )rp  r   )rg   r]   s     r5   get_all_modelszParlerOptions.get_all_models  s"     (,'7'78t

888s   %c                 H    t        | j                  j                               S )zJ
        Return all translated fields associated with this model.
        )rO   rq  keysr   s    r5   rr   zParlerOptions.get_all_fields  s     D))..011r4   c                 6    | j                   j                         S )zV
        Convenience function, return all translated fields with their model.
        )rq  r   r   s    r5   get_fields_with_modelz#ParlerOptions.get_fields_with_model  s     $$**,,r4   Nc                 H    | j                  |      }|j                  |      S )z
        Return the translated fields of this model.
        By default, the top-level translation is required, unless ``related_name`` is provided.
        rh  )r   r'  )rg   rI   rF  r]   s       r5   r'  z#ParlerOptions.get_translated_fields  s(     22<@))k)BBr4   c                 Z    	 | j                   |   S # t        $ r t        d| d      w xY w)zX
        Find the :class:`TranslatedFieldsModel` that contains the given field.
        z"Translated field does not exist: 'rK  )rq  rs   r   rg   r_   s     r5   get_model_by_fieldz ParlerOptions.get_model_by_field  s?    	K((.. 	KA$qIJJ	Ks    *c                 <    | j                  |      }|j                  S rf   )r   r   rg   rI   r]   s      r5   get_model_by_related_namez'ParlerOptions.get_model_by_related_name  s    22<@zzr4   c                 @    t        fd| j                  D              S )Nc              3   <   K   | ]  }|j                   k(    y wrf   rz  )r   r]   r   s     r5   r   z8ParlerOptions._has_translations_model.<locals>.<genexpr>  s     D44::&D   anyrp  )rg   r   s    `r5   _has_translations_modelz%ParlerOptions._has_translations_model  s    D43C3CDDDr4   c                 @    t        fd| j                  D              S )Nc              3   <   K   | ]  }|j                   k(    y wrf   )r   )r   r]   r_   s     r5   r   z8ParlerOptions._has_translations_field.<locals>.<genexpr>
  s     FT4==D(Fr  r  r  s    `r5   _has_translations_fieldz%ParlerOptions._has_translations_field	  s    FT5E5EFFFr4   c                     |t        d      | j                  |      }| j                  D ]  }|j                  |k(  s|c S  y)za
        Find the ParlerOptions object that corresponds with the given translated field.
        NzExpected field name)rN   r  rp  r   )rg   r_   r   r]   s       r5   r   z%ParlerOptions._get_extension_by_field  sM     <122 **40$$ 	DzzX%	r4   c                     || j                   d   S | j                   D ]  }|j                  |k(  s|c S  t        dj                  | j                  j
                  j                  |            )z
        Find which model is connected to a given related name.
        If the related name is ``None``, the :attr:`root_model` will be returned.
        r   z6No translated model of '{}' has a reverse name of '{}')rp  r   r   r7   r   r\   r/   r  s      r5   r   z,ParlerOptions._get_extension_by_related_name  ss    
 ##A&&$$ 	D}},	 DKK		&&//
 	
r4   c              +      K   | j                   D ]2  }i }|j                  j                         D ]  }	 ||   ||<    ||f 4 y # t        $ r Y "w xY wwrf   )rp  r   r'  rs   )rg   r^   r]   r   r{   s        r5   r   zParlerOptions._split_fields+  so     $$ 	'DL99; *0-L' &&	'
   s'   /AAA	AAAA)NT)r/   r0   r1   r2   rh   rL  rU  rW  r   rw  r}  r  r  rr   r  r'  r  r  r  r  r   r   r   r3   r4   r5   rN  rN    sv    RB=
 # #&7%92-CKEG
$
'r4   rN  )Gr2   rY   r  collectionsr   r   django.confr   django.core.exceptionsr   r   r   r	   	django.dbr
   r   django.db.models.baser   +django.db.models.fields.related_descriptorsr   r   django.utils.encodingr   django.utils.functionalr   django.utils.translationr   r   rb  parlerr   parler.cacher   r   r   r   r   r   r   r   parler.fieldsr   r   r   r   r    parler.managersr!   parler.utilsr"   parler.utils.i18nr#   r$   r%   r&   r'   __all__r   r-   rS   rR   rc   r*   r(   r  r)   r,   r  r+   rM  rN  r3   r4   r5   <module>r     s  6n   0    % + , ( , 6 	 	 	  0  	n.@ 	( []`a >B3G 3GlR	' R	'j$. $$	 $NN
 N
b	!8Q	!
 
2r' r'r4   