B
    CVvgä1  ã               @   s‚   d dl Z d dlmZmZ d dlmZ d dlmZ d dlm	Z	 d dl
mZ d dlmZmZmZmZ G dd	„ d	ƒZG d
d„ deƒZdS )é    N)ÚNoReverseMatchÚreverse)Ú	mark_safe)ÚFormHelpersException)ÚLayout)ÚLayoutSlice)ÚTEMPLATE_PACKÚflatattÚlist_differenceÚrender_fieldc               @   sd   e Zd Zdd„ Zdd„ Zdd„ Zddd	œd
d„Zdd„ Zdd„ Zdd„ Z	dd„ Z
dd„ Zdd„ ZdS )ÚDynamicLayoutHandlerc             C   s   | j d krtdƒ‚d S )Nz+You need to set a layout in your FormHelper)Úlayoutr   )Úself© r   úD/tmp/pip-install-o3oxmrkh/django-crispy-forms/crispy_forms/helper.pyÚ_check_layout   s    
z"DynamicLayoutHandler._check_layoutc             C   s   |   ¡  | jd krtdƒ‚d S )Nz3You need to pass a form instance to your FormHelper)r   Úformr   )r   r   r   r   Ú_check_layout_and_form   s    
z+DynamicLayoutHandler._check_layout_and_formc             C   s$   |   ¡  t| jtdt| jjƒdƒƒS )zD
        Returns all layout objects of first level of depth
        r   é   )r   r   r   ÚsliceÚlenÚfields)r   r   r   r   Úall   s    zDynamicLayoutHandler.allr   F)Ú	max_levelÚgreedyc            G   s&   |   ¡  | jj|||d�}t| j|ƒS )zX
        Returns a LayoutSlice pointing to layout objects of type `LayoutClass`
        )r   r   )r   r   Zget_layout_objectsr   )r   r   r   ZLayoutClassesZfiltered_layout_objectsr   r   r   Úfilter   s    zDynamicLayoutHandler.filterc             C   sP   |   ¡  | j ¡ }g }x,|D ]$}t| jj|j j|ƒr| |¡ qW t	| j|ƒS )zX
        Returns a LayoutSlice pointing to fields with widgets of `widget_type`
        )
r   r   Úget_field_namesÚ
isinstancer   r   ÚnameÚwidgetÚappendr   )r   Úwidget_typeÚlayout_field_namesÚfiltered_fieldsÚpointerr   r   r   Úfilter_by_widget&   s    

z%DynamicLayoutHandler.filter_by_widgetc             C   sP   |   ¡  | j ¡ }g }x,|D ]$}t| jj|j j|ƒs| |¡ qW t	| j|ƒS )zb
        Returns a LayoutSlice pointing to fields with widgets NOT matching `widget_type`
        )
r   r   r   r   r   r   r   r   r    r   )r   r!   r"   r#   r$   r   r   r   Úexclude_by_widget5   s    

z&DynamicLayoutHandler.exclude_by_widgetc             C   sn   t |tƒrbt| |ƒrt| |ƒS |  ¡  | j ¡ }g }x |D ]}|j|kr:| |¡ q:W t	| j|ƒS t	| j|ƒS )z{
        Return a LayoutSlice that makes changes affect the current instance of the layout
        and not a copy.
        )
r   ÚstrÚhasattrÚgetattrr   r   r   r   r    r   )r   Úkeyr"   Zfiltered_fieldr$   r   r   r   Ú__getitem__D   s    





z DynamicLayoutHandler.__getitem__c             C   s   || j |< d S )N)r   )r   r*   Úvaluer   r   r   Ú__setitem__]   s    z DynamicLayoutHandler.__setitem__c             C   s   | j j|= d S )N)r   r   )r   r*   r   r   r   Ú__delitem__`   s    z DynamicLayoutHandler.__delitem__c             C   s   | j d k	rt| j jƒS dS d S )Nr   )r   r   r   )r   r   r   r   Ú__len__c   s    
zDynamicLayoutHandler.__len__N)Ú__name__Ú
__module__Ú__qualname__r   r   r   r   r%   r&   r+   r-   r.   r/   r   r   r   r   r      s   	r   c               @   s  e Zd ZdZdZdZdZdZdZdZ	dZ
dZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZdZddd„Zd	d
„ Zedd„ ƒZej dd„ ƒZedd„ ƒZ!e!j dd„ ƒZ!edd„ ƒZ"e"j dd„ ƒZ"edd„ ƒZ#e#j dd„ ƒZ#dd„ Z$dd„ Z%e&fdd„Z'e&fdd„Z(dS ) Ú
FormHelperaq  
    This class controls the form rendering behavior of the form passed to
    the `{% crispy %}` tag. For doing so you will need to set its attributes
    and pass the corresponding helper object to the tag::

        {% crispy form form.helper %}

    Let's see what attributes you can set and what form behaviors they apply to:

        **form_method**: Specifies form method attribute.
            You can set it to 'POST' or 'GET'. Defaults to 'POST'

        **form_action**: Applied to the form action attribute:
            - Can be a named url in your URLconf that can be executed via the `{% url %}` template tag.             Example: 'show_my_profile'. In your URLconf you could have something like::

                path('show/profile/', 'show_my_profile_view', name = 'show_my_profile')

            - It can simply point to a URL '/whatever/blabla/'.

        **form_id**: Generates a form id for dom identification.
            If no id provided then no id attribute is created on the form.

        **form_class**: String containing separated CSS classes to be applied
            to form class attribute.

        **form_group_wrapper_class**: String containing separated CSS classes to be applied
            to each row of inputs.

        **form_tag**: It specifies if <form></form> tags should be rendered when using a Layout.
            If set to False it renders the form without the <form></form> tags. Defaults to True.

        **form_error_title**: If a form has `non_field_errors` to display, they
            are rendered in a div. You can set title's div with this attribute.
            Example: "Oooops!" or "Form Errors"

        **formset_error_title**: If a formset has `non_form_errors` to display, they
            are rendered in a div. You can set title's div with this attribute.

        **include_media**: Whether to automatically include form media. Set to False if
            you want to manually include form media outside the form. Defaults to True.

    Public Methods:

        **add_input(input)**: You can add input buttons using this method. Inputs
            added using this method will be rendered at the end of the form/formset.

        **add_layout(layout)**: You can add a `Layout` object to `FormHelper`. The Layout
            specifies in a simple, clean and DRY way how the form fields should be rendered.
            You can wrap fields, order them, customize pretty much anything in the form.

    Best way to add a helper to a form is adding a property named helper to the form
    that returns customized `FormHelper` object::

        from crispy_forms.helper import FormHelper
        from crispy_forms.layout import Submit

        class MyForm(forms.Form):
            title = forms.CharField(_("Title"))

            @property
            def helper(self):
                helper = FormHelper()
                helper.form_id = 'this-form-rocks'
                helper.form_class = 'search'
                helper.add_input(Submit('save', 'save'))
                [...]
                return helper

    You can use it in a template doing::

        {% load crispy_forms_tags %}
        {% crispy form %}
    ÚpostÚ NTFc             C   s*   i | _ g | _|d k	r&|| _|  |¡| _d S )N)ÚattrsÚinputsr   Úbuild_default_layoutr   )r   r   r   r   r   Ú__init__Ð   s
    zFormHelper.__init__c             C   s   t |j ¡ Ž S )N)r   r   Úkeys)r   r   r   r   r   r8   Ø   s    zFormHelper.build_default_layoutc             C   s   | j S )N)Ú_form_method)r   r   r   r   Úform_methodÛ   s    zFormHelper.form_methodc             C   s"   |  ¡ dkrtdƒ‚|  ¡ | _d S )N)Úgetr4   zSOnly GET and POST are valid in the                     form_method helper attribute)Úlowerr   r;   )r   Úmethodr   r   r   r<   ß   s    c             C   s&   y
t | jƒS  tk
r    | jS X d S )N)r   Ú_form_actionr   )r   r   r   r   Úform_actioné   s    
zFormHelper.form_actionc             C   s
   || _ d S )N)r@   )r   Úactionr   r   r   rA   ð   s    c             C   s   | j S )N)Ú_help_text_inline)r   r   r   r   Úhelp_text_inlineô   s    zFormHelper.help_text_inlinec             C   s   || _ | | _d S )N)rC   Ú_error_text_inline)r   Úflagr   r   r   rD   ø   s    c             C   s   | j S )N)rE   )r   r   r   r   Úerror_text_inlineý   s    zFormHelper.error_text_inlinec             C   s   || _ | | _d S )N)rE   rC   )r   rF   r   r   r   rG     s    c             C   s   | j  |¡ d S )N)r7   r    )r   Zinput_objectr   r   r   Ú	add_input  s    zFormHelper.add_inputc             C   s
   || _ d S )N)r   )r   r   r   r   r   Ú
add_layout	  s    zFormHelper.add_layoutc             C   s¦   t ƒ |_| j|_| jj|||d�}| js4| js4| jržt	|j
 ¡ ƒ}t||jƒ}xN|D ]F}| js†| jrr|j
| jjs†| jrT|j
| jjrT|t||||d�7 }qTW t|ƒS )zB
        Returns safe html of the rendering of the layout
        )Útemplate_pack)ÚsetZrendered_fieldsÚfield_templateZcrispy_field_templater   ÚrenderÚrender_unmentioned_fieldsÚrender_hidden_fieldsÚrender_required_fieldsÚtupler   r:   r
   r   Z	is_hiddenZis_requiredr   r   )r   r   ÚcontextrJ   Úhtmlr   Zleft_fields_to_renderÚfieldr   r   r   Úrender_layout  s    
zFormHelper.render_layoutc                s„  | j r| j  ¡ ni }| jr(| j ¡ |d< | jr<| j ¡ |d< | jrP| j ¡ |d< | jr`| j|d< || j| j| j	| j
pzd| t|ƒ| j ¡ | j ¡ | j| j| j| j ¡ | j| j| j| jdœ}|dkrþd| j ¡ krüt d	| j¡}|rüd
‰ ‡ fdd„|D ƒ|d< n.t d| j¡}|�r,d‰ ‡ fdd„|D ƒ|d< | j�r>| j|d< x@| j ¡ D ]2\}}||k�rJ|dk�rJ| d¡�sJ|||< �qJW |S )zD
        Used by crispy_forms_tags to get helper attributes
        rB   ÚidÚclassÚform_group_wrapper_classz%s/field.html)r6   Údisable_csrfrG   Úfield_classrL   Z
flat_attrsÚform_error_titler<   Úform_show_errorsÚform_show_labelsÚform_tagÚformset_error_titlerD   Úinclude_mediaÚlabel_classÚuse_custom_controlZ
bootstrap4zform-horizontalzcol(-(xl|lg|md|sm))?-(\d+)zoffset%s-%sc                s    g | ]}ˆ |d  |d f ‘qS )r   éÿÿÿÿr   )Ú.0Úm)Úoffset_patternr   r   ú
<listcomp>K  s    z-FormHelper.get_attributes.<locals>.<listcomp>Zbootstrap_checkbox_offsetszcol-(lg|md|sm|xs)-(\d+)zcol-%s-offset-%sc                s   g | ]}ˆ | ‘qS r   r   )rd   re   )rf   r   r   rg   Q  s    r7   )r   r7   Ú_)r6   ÚcopyrA   ÚstripÚform_idÚ
form_classrX   rY   rG   rZ   rL   r	   r[   r<   r\   r]   r^   r_   rD   r`   ra   rb   ÚsplitÚreÚfindallr7   Ú__dict__ÚitemsÚ
startswith)r   rJ   r6   rq   Zbootstrap_size_matchZattribute_namer,   r   )rf   r   Úget_attributes$  sV    




zFormHelper.get_attributes)N))r0   r1   r2   Ú__doc__r;   r@   r   rk   rl   rX   r   r^   r[   r_   r\   rN   rO   rP   rC   rE   r]   ÚtemplaterL   rY   rb   ra   rZ   r`   r9   r8   Úpropertyr<   ÚsetterrA   rD   rG   rH   rI   r   rU   rs   r   r   r   r   r3   j   sN   J

r3   )rn   Zdjango.urlsr   r   Zdjango.utils.safestringr   Zcrispy_forms.exceptionsr   Zcrispy_forms.layoutr   Zcrispy_forms.layout_slicer   Zcrispy_forms.utilsr   r	   r
   r   r   r3   r   r   r   r   Ú<module>   s   ^