U
    PN^B                     @   s   d Z ddlmZmZmZ ddlmZ ddlm	Z	m
Z
mZmZ dd Zdd ZG d	d
 d
eZG dd deZdddZeddddfddZdS )aH  
Form generation utilities for App Engine's new ``ndb.Model`` class.

The goal of ``model_form()`` is to provide a clean, explicit and predictable
way to create forms based on ``ndb.Model`` classes. No malabarism or black
magic should be necessary to generate a form for models, and to add custom
non-model related fields: ``model_form()`` simply generates a form class
that can be used as it is, or that can be extended directly or even be used
to create other forms using ``model_form()``.

Example usage:

.. code-block:: python

   from google.appengine.ext import ndb
   from wtforms.ext.appengine.ndb import model_form

   # Define an example model and add a record.
   class Contact(ndb.Model):
       name = ndb.StringProperty(required=True)
       city = ndb.StringProperty()
       age = ndb.IntegerProperty(required=True)
       is_admin = ndb.BooleanProperty(default=False)

   new_entity = Contact(key_name='test', name='Test Name', age=17)
   new_entity.put()

   # Generate a form based on the model.
   ContactForm = model_form(Contact)

   # Get a form populated with entity data.
   entity = Contact.get_by_key_name('test')
   form = ContactForm(obj=entity)

Properties from the model can be excluded from the generated form, or it can
include just a set of properties. For example:

.. code-block:: python

   # Generate a form based on the model, excluding 'city' and 'is_admin'.
   ContactForm = model_form(Contact, exclude=('city', 'is_admin'))

   # or...

   # Generate a form based on the model, only including 'name' and 'age'.
   ContactForm = model_form(Contact, only=('name', 'age'))

The form can be generated setting field arguments:

.. code-block:: python

   ContactForm = model_form(Contact, only=('name', 'age'), field_args={
       'name': {
           'label': 'Full name',
           'description': 'Your name',
       },
       'age': {
           'label': 'Age',
           'validators': [validators.NumberRange(min=14, max=99)],
       }
   })

The class returned by ``model_form()`` can be used as a base class for forms
mixing non-model fields and/or other model forms. For example:

.. code-block:: python

   # Generate a form based on the model.
   BaseContactForm = model_form(Contact)

   # Generate a form based on other model.
   ExtraContactForm = model_form(MyOtherModel)

   class ContactForm(BaseContactForm):
       # Add an extra, non-model related field.
       subscribe_to_news = f.BooleanField()

       # Add the other model form as a subform.
       extra = f.FormField(ExtraContactForm)

The class returned by ``model_form()`` can also extend an existing form
class:

.. code-block:: python

   class BaseContactForm(Form):
       # Add an extra, non-model related field.
       subscribe_to_news = f.BooleanField()

   # Generate a form based on the model.
   ContactForm = model_form(Contact, base_class=BaseContactForm)

    )Form
validatorsfields)string_types)GeoPtPropertyFieldKeyPropertyFieldStringListPropertyFieldIntegerListPropertyFieldc                 C   s"   | d  tjdd tjf | S )ze
    Returns a ``TextField``, applying the ``ndb.StringProperty`` length limit
    of 500 bytes.
    r     max)appendr   lengthfZ	TextField)kwargs r   >/tmp/pip-install-o1yuzyg2/WTForms/wtforms/ext/appengine/ndb.pyget_TextFieldc   s    r   c                 C   s(   t jddd}| d | tjf | S )z]
    Returns an ``IntegerField``, applying the ``ndb.IntegerProperty`` range
    limits.
    l         l    )minr   r   )r   ZNumberRanger   r   ZIntegerField)r   vr   r   r   get_IntegerFieldl   s    r   c                   @   s   e Zd ZdddZdd ZdS )ModelConverterBaseNc                 C   s<   i | _ t| D ](}|dsqt| || j |dd < qdS )z
        Constructs the converter, setting the converter callables.

        :param converters:
            A dictionary of converter callables for each property type. The
            callable must accept the arguments (model, prop, kwargs).
        Zconvert_   N)
convertersdir
startswithgetattr)selfr   namer   r   r   __init__w   s
    
zModelConverterBase.__init__c                 C   s   t |j}|dkr*|d}|r*|d}|jdd |jg d}|rT|| |jrv|| j	krv|d 
t  |ddrd	d
 |dD |d< tjf |S |jrdd
 |jD |d< tjf |S | j|d}|dk	r||||S | |||S dS )aD  
        Returns a form field for a single model property.

        :param model:
            The ``db.Model`` class that contains the property.
        :param prop:
            The model property: a ``db.Property`` instance.
        :param field_args:
            Optional keyword arguments to construct the field.
        ZGenericPropertytype_ )labeldefaultr   r   choicesNc                 S   s   g | ]}||fqS r   r   .0r   r   r   r   
<listcomp>   s     z.ModelConverterBase.convert.<locals>.<listcomp>c                 S   s   g | ]}||fqS r   r   r&   r   r   r   r(      s     )r    __name__getZ
_code_namereplacetitle_defaultupdate	_requiredNO_AUTO_REQUIREDr   r   requiredr   ZSelectFieldZ_choicesr   Zfallback_converter)r   modelprop
field_argsZprop_type_nameZgeneric_typer   	converterr   r   r   convert   s.    



zModelConverterBase.convert)N)r)   
__module____qualname__r   r6   r   r   r   r   r   v   s   
r   c                   @   s   e Zd ZdZedddg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d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d'd( Zd)d* Zd+S ),ModelConvertera?  
    Converts properties from a ``ndb.Model`` class to form fields.

    Default conversions between properties and fields:

    +====================+===================+==============+==================+
    | Property subclass  | Field subclass    | datatype     | notes            |
    +====================+===================+==============+==================+
    | StringProperty     | TextField         | unicode      | TextArea         | repeated support
    |                    |                   |              | if multiline     |
    +--------------------+-------------------+--------------+------------------+
    | BooleanProperty    | BooleanField      | bool         |                  |
    +--------------------+-------------------+--------------+------------------+
    | IntegerProperty    | IntegerField      | int or long  |                  | repeated support
    +--------------------+-------------------+--------------+------------------+
    | FloatProperty      | TextField         | float        |                  |
    +--------------------+-------------------+--------------+------------------+
    | DateTimeProperty   | DateTimeField     | datetime     | skipped if       |
    |                    |                   |              | auto_now[_add]   |
    +--------------------+-------------------+--------------+------------------+
    | DateProperty       | DateField         | date         | skipped if       |
    |                    |                   |              | auto_now[_add]   |
    +--------------------+-------------------+--------------+------------------+
    | TimeProperty       | DateTimeField     | time         | skipped if       |
    |                    |                   |              | auto_now[_add]   |
    +--------------------+-------------------+--------------+------------------+
    | TextProperty       | TextAreaField     | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | GeoPtProperty      | TextField         | db.GeoPt     |                  |
    +--------------------+-------------------+--------------+------------------+
    | KeyProperty        | KeyProperyField   | ndb.Key      |                  |
    +--------------------+-------------------+--------------+------------------+
    | BlobKeyProperty    | None              | ndb.BlobKey  | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | UserProperty       | None              | users.User   | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | StructuredProperty | None              | ndb.Model    | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | LocalStructuredPro | None              | ndb.Model    | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | JsonProperty       | TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | PickleProperty     | None              | bytedata     | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | GenericProperty    | None              | generic      | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | ComputedProperty   | none              |              | always skipped   |
    +====================+===================+==============+==================+

    ZListPropertyZStringListPropertyZBooleanPropertyc                 C   s.   |j rtf |S |d tjdd t|S )z2Returns a form field for a ``ndb.StringProperty``.r   r
   r   )	_repeatedr   r   r   r   r   r   r2   r3   r   r   r   r   convert_StringProperty   s    
z%ModelConverter.convert_StringPropertyc                 C   s   t jf |S )z3Returns a form field for a ``ndb.BooleanProperty``.)r   ZBooleanFieldr;   r   r   r   convert_BooleanProperty   s    z&ModelConverter.convert_BooleanPropertyc                 C   s   |j rtf |S t|S )z3Returns a form field for a ``ndb.IntegerProperty``.)r:   r	   r   r;   r   r   r   convert_IntegerProperty   s    
z&ModelConverter.convert_IntegerPropertyc                 C   s   t jf |S )z1Returns a form field for a ``ndb.FloatProperty``.)r   Z
FloatFieldr;   r   r   r   convert_FloatProperty  s    z$ModelConverter.convert_FloatPropertyc                 C   s$   |j s|jrdS tjf ddi|S )z4Returns a form field for a ``ndb.DateTimeProperty``.Nformatz%Y-%m-%d %H:%M:%S	_auto_now_auto_now_addr   ZDateTimeFieldr;   r   r   r   convert_DateTimeProperty  s    z'ModelConverter.convert_DateTimePropertyc                 C   s$   |j s|jrdS tjf ddi|S )z0Returns a form field for a ``ndb.DateProperty``.Nr@   z%Y-%m-%d)rB   rC   r   Z	DateFieldr;   r   r   r   convert_DateProperty  s    z#ModelConverter.convert_DatePropertyc                 C   s$   |j s|jrdS tjf ddi|S )z0Returns a form field for a ``ndb.TimeProperty``.Nr@   z%H:%M:%SrA   r;   r   r   r   convert_TimeProperty  s    z#ModelConverter.convert_TimePropertyc                 C   s   dS 0Returns a form field for a ``ndb.ListProperty``.Nr   r;   r   r   r   convert_RepeatedProperty  s    z'ModelConverter.convert_RepeatedPropertyc                 C   s   dS )z0Returns a form field for a ``ndb.UserProperty``.Nr   r;   r   r   r   convert_UserProperty  s    z#ModelConverter.convert_UserPropertyc                 C   s   dS rG   r   r;   r   r   r   convert_StructuredProperty"  s    z)ModelConverter.convert_StructuredPropertyc                 C   s   dS rG   r   r;   r   r   r   convert_LocalStructuredProperty&  s    z.ModelConverter.convert_LocalStructuredPropertyc                 C   s   dS rG   r   r;   r   r   r   convert_JsonProperty*  s    z#ModelConverter.convert_JsonPropertyc                 C   s   dS rG   r   r;   r   r   r   convert_PickleProperty.  s    z%ModelConverter.convert_PicklePropertyc                 C   s   |d  tjdd t|S )rH   r   r
   r   )r   r   r   r   r;   r   r   r   convert_GenericProperty2  s    z&ModelConverter.convert_GenericPropertyc                 C   s   t jf |S )z3Returns a form field for a ``ndb.BlobKeyProperty``.)r   Z	FileFieldr;   r   r   r   convert_BlobKeyProperty7  s    z&ModelConverter.convert_BlobKeyPropertyc                 C   s   t jf |S )z0Returns a form field for a ``ndb.TextProperty``.)r   ZTextAreaFieldr;   r   r   r   convert_TextProperty;  s    z#ModelConverter.convert_TextPropertyc                 C   s   dS )z4Returns a form field for a ``ndb.ComputedProperty``.Nr   r;   r   r   r   convert_ComputedProperty?  s    z'ModelConverter.convert_ComputedPropertyc                 C   s
   t f |S )z1Returns a form field for a ``ndb.GeoPtProperty``.)r   r;   r   r   r   convert_GeoPtPropertyC  s    z$ModelConverter.convert_GeoPtPropertyc                 C   sx   d|kr^z
|j }W n tk
r,   |j}Y nX t|trVt|jdd|gd}t||}||d< |d|j	  t
f |S )z/Returns a form field for a ``ndb.KeyProperty``.reference_classNr   Zallow_blank)_kindAttributeErrorZ_reference_class
isinstancer   
__import__r7   r   
setdefaultr/   r   )r   r2   r3   r   rT   modr   r   r   convert_KeyPropertyG  s    


z"ModelConverter.convert_KeyPropertyN)r)   r7   r8   __doc__	frozensetr0   r<   r=   r>   r?   rD   rE   rF   rI   rJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   r[   r   r   r   r   r9      s*   3r9   Nc           	         s   |pt  }|pi }| j}tdd t| dd dD |rVtfdd|D n rpt fddD i }D ],}|| || ||}|dk	rx|||< qx|S )	a  
    Extracts and returns a dictionary of form fields for a given
    ``db.Model`` class.

    :param model:
        The ``db.Model`` class to extract fields from.
    :param only:
        An optional iterable with the property names that should be included in
        the form. Only these properties will have fields.
    :param exclude:
        An optional iterable with the property names that should be excluded
        from the form. All other properties will have fields.
    :param field_args:
        An optional dictionary of field names mapping to a keyword arguments
        used to construct each field object.
    :param converter:
        A converter to generate the fields based on the model properties. If
        not set, ``ModelConverter`` is used.
    c                 s   s   | ]}|d  V  qdS )r   Nr   )r'   xr   r   r   	<genexpr>s  s     zmodel_fields.<locals>.<genexpr>c                 S   s
   | d j S )N   )Z_creation_counter)r^   r   r   r   <lambda>s      zmodel_fields.<locals>.<lambda>)keyc                 3   s   | ]}| kr|V  qd S Nr   r'   r   )field_namesr   r   r_   v  s      c                 3   s   | ]}| kr|V  qd S rd   r   re   )excluder   r   r_   x  s      N)r9   Z_propertieslistsorteditemsr6   r*   )	r2   onlyrg   r4   r5   props
field_dictr   fieldr   )rg   rf   r   model_fieldsX  s    
"
ro   c                 C   s&   t | ||||}t|  d |f|S )a.  
    Creates and returns a dynamic ``wtforms.Form`` class for a given
    ``ndb.Model`` class. The form class can be used as it is or serve as a base
    for extended form classes, which can then mix non-model related fields,
    subforms with other model forms, among other possibilities.

    :param model:
        The ``ndb.Model`` class to generate a form for.
    :param base_class:
        Base form class to extend from. Must be a ``wtforms.Form`` subclass.
    :param only:
        An optional iterable with the property names that should be included in
        the form. Only these properties will have fields.
    :param exclude:
        An optional iterable with the property names that should be excluded
        from the form. All other properties will have fields.
    :param field_args:
        An optional dictionary of field names mapping to keyword arguments
        used to construct each field object.
    :param converter:
        A converter to generate the fields based on the model properties. If
        not set, ``ModelConverter`` is used.
    r   )ro   r    Z	_get_kind)r2   Z
base_classrk   rg   r4   r5   rm   r   r   r   
model_form  s    rp   )NNNN)r\   Zwtformsr   r   r   r   Zwtforms.compatr   Zwtforms.ext.appengine.fieldsr   r   r   r	   r   r   objectr   r9   ro   rp   r   r   r   r   <module>   s   ]	
D   
,