o
    [hB                     @   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di | S )ze
    Returns a ``TextField``, applying the ``ndb.StringProperty`` length limit
    of 500 bytes.
    r     maxN )appendr   lengthfZ	TextField)kwargsr   r   /home/ubuntu/experiments/live_experiments/Pythonexperiments/Otree/venv/lib/python3.10/site-packages/wtforms/ext/appengine/ndb.pyget_TextFieldc   s   r   c                 C   s,   t jddd}| d | tjdi | S )z]
    Returns an ``IntegerField``, applying the ``ndb.IntegerProperty`` range
    limits.
    l         l    )minr   r   Nr   )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}|r*|| |jr;|| j	vr;|d 
t  |ddrUd	d
 |dD |d< tjdi |S |jrjdd
 |jD |d< tjdi |S | j|d}|dur{||||S | |||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      g | ]}||fqS r   r   .0r   r   r   r   
<listcomp>       z.ModelConverterBase.convert.<locals>.<listcomp>c                 S   r&   r   r   r'   r   r   r   r)      r*   r   )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   r8   r   r   r   r   r   v   s    
r   c                   @   s   e Zd ZdZeg 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'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   s2   |j r
tdi |S |d tjdd t|S )z2Returns a form field for a ``ndb.StringProperty``.r   r
   r   Nr   )	_repeatedr   r   r   r   r   r   r4   r5   r   r   r   r   convert_StringProperty   s   z%ModelConverter.convert_StringPropertyc                 C      t jdi |S )z3Returns a form field for a ``ndb.BooleanProperty``.Nr   )r   ZBooleanFieldr>   r   r   r   convert_BooleanProperty      z&ModelConverter.convert_BooleanPropertyc                 C   s   |j r
tdi |S t|S )z3Returns a form field for a ``ndb.IntegerProperty``.Nr   )r=   r	   r   r>   r   r   r   convert_IntegerProperty   s   z&ModelConverter.convert_IntegerPropertyc                 C   r@   )z1Returns a form field for a ``ndb.FloatProperty``.Nr   )r   Z
FloatFieldr>   r   r   r   convert_FloatProperty  rB   z$ModelConverter.convert_FloatPropertyc                 C   $   |j s|jrdS tjdddi|S )z4Returns a form field for a ``ndb.DateTimeProperty``.Nformatz%Y-%m-%d %H:%M:%Sr   	_auto_now_auto_now_addr   ZDateTimeFieldr>   r   r   r   convert_DateTimeProperty     z'ModelConverter.convert_DateTimePropertyc                 C   rE   )z0Returns a form field for a ``ndb.DateProperty``.NrF   z%Y-%m-%dr   )rH   rI   r   Z	DateFieldr>   r   r   r   convert_DateProperty  rK   z#ModelConverter.convert_DatePropertyc                 C   rE   )z0Returns a form field for a ``ndb.TimeProperty``.NrF   z%H:%M:%Sr   rG   r>   r   r   r   convert_TimeProperty  rK   z#ModelConverter.convert_TimePropertyc                 C      dS 0Returns a form field for a ``ndb.ListProperty``.Nr   r>   r   r   r   convert_RepeatedProperty     z'ModelConverter.convert_RepeatedPropertyc                 C   rN   )z0Returns a form field for a ``ndb.UserProperty``.Nr   r>   r   r   r   convert_UserProperty  rR   z#ModelConverter.convert_UserPropertyc                 C   rN   rO   r   r>   r   r   r   convert_StructuredProperty"  rR   z)ModelConverter.convert_StructuredPropertyc                 C   rN   rO   r   r>   r   r   r   convert_LocalStructuredProperty&  rR   z.ModelConverter.convert_LocalStructuredPropertyc                 C   rN   rO   r   r>   r   r   r   convert_JsonProperty*  rR   z#ModelConverter.convert_JsonPropertyc                 C   rN   rO   r   r>   r   r   r   convert_PickleProperty.  rR   z%ModelConverter.convert_PicklePropertyc                 C   s   |d  tjdd t|S )rP   r   r
   r   )r   r   r   r   r>   r   r   r   convert_GenericProperty2  s   z&ModelConverter.convert_GenericPropertyc                 C   r@   )z3Returns a form field for a ``ndb.BlobKeyProperty``.Nr   )r   Z	FileFieldr>   r   r   r   convert_BlobKeyProperty7  rB   z&ModelConverter.convert_BlobKeyPropertyc                 C   r@   )z0Returns a form field for a ``ndb.TextProperty``.Nr   )r   ZTextAreaFieldr>   r   r   r   convert_TextProperty;  rB   z#ModelConverter.convert_TextPropertyc                 C   rN   )z4Returns a form field for a ``ndb.ComputedProperty``.Nr   r>   r   r   r   convert_ComputedProperty?  rR   z'ModelConverter.convert_ComputedPropertyc                 C   s   t di |S )z1Returns a form field for a ``ndb.GeoPtProperty``.Nr   )r   r>   r   r   r   convert_GeoPtPropertyC  s   z$ModelConverter.convert_GeoPtPropertyc                 C   sz   d|vr.z|j }W n ty   |j}Y nw t|tr*t|jdd|gd}t||}||d< |d|j	  t
di |S )z/Returns a form field for a ``ndb.KeyProperty``.reference_classNr   allow_blankr   )_kindAttributeErrorZ_reference_class
isinstancer   
__import__r:   r   
setdefaultr1   r   )r   r4   r5   r   r]   modr   r   r   convert_KeyPropertyG  s   



z"ModelConverter.convert_KeyPropertyN)r+   r:   r;   __doc__	frozensetr2   r?   rA   rC   rD   rJ   rL   rM   rQ   rS   rT   rU   rV   rW   rX   rY   rZ   r[   r\   re   r   r   r   r   r<      s,    3r<   Nc           	         s   |pt  }|pi }| j}tdd t| dd dD |r+tfdd|D n r8t fddD i }D ]}|| || ||}|durR|||< q<|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)rh   r   r   r   <lambda>s  s   
 zmodel_fields.<locals>.<lambda>)keyc                 3   s    | ]	}| v r|V  qd S r9   r   r(   r   )field_namesr   r   ri   v      c                 3   s    | ]	}| vr|V  qd S r9   r   rm   )excluder   r   ri   x  ro   N)r<   Z_propertieslistsorteditemsr8   r,   )	r4   onlyrp   r6   r7   props
field_dictr   fieldr   )rp   rn   r   model_fieldsX  s   
"rx   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   )rx   r    Z	_get_kind)r4   Z
base_classrt   rp   r6   r7   rv   r   r   r   
model_form  s   ry   )NNNN)rf   Zwtformsr   r   r   r   Zwtforms.compatr   Zwtforms.ext.appengine.fieldsr   r   r   r	   r   r   objectr   r<   rx   ry   r   r   r   r   <module>   s    ]	
D 
,