
    wdfH                        d Z ddlmZmZmZmZ ddlmZ ddl	m
Z
mZm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% G d de&          Z'	 	 d#d!Z(ed d d d fd"Z)d S )$a7  
Form generation utilities for App Engine's ``db.Model`` class.

The goal of ``model_form()`` is to provide a clean, explicit and predictable
way to create forms based on ``db.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 db
   from tipfy.ext.model.form import model_form

   # Define an example model and add a record.
   class Contact(db.Model):
       name = db.StringProperty(required=True)
       city = db.StringProperty()
       age = db.IntegerProperty(required=True)
       is_admin = db.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widgetsfields)	iteritems)GeoPtPropertyFieldReferencePropertyFieldStringListPropertyFieldc                     | d                              t          j        d                     t          j        di | S )zd
    Returns a ``TextField``, applying the ``db.StringProperty`` length limit
    of 500 bytes.
    r     max )appendr   lengthf	TextField)kwargss    ]/var/www/book.euthymeo.com/html/venv/lib/python3.11/site-packages/wtforms/ext/appengine/db.pyget_TextFieldr   c   sA    
 <
 1c : : :;;;;         c                     t          j        dd          }| d                             |           t          j        di | S )z\
    Returns an ``IntegerField``, applying the ``db.IntegerProperty`` range
    limits.
    l         l    minr   r   r   )r   NumberRanger   r   IntegerField)r   vs     r   get_IntegerFieldr   l   sJ    
 	#6<NOOOA
<""">##F###r   c                     |j         r?|d                             t          j        d                     t	          j        di |S t          |          S )z1Returns a form field for a ``db.StringProperty``.r   r   r   Nr   )	multiliner   r   r   r   TextAreaFieldr   modelpropr   s      r   convert_StringPropertyr%   v   sW    ~ %|##J$5#$>$>$>???(((((V$$$r   c                      t          |          S )z5Returns a form field for a ``db.ByteStringProperty``.r   r"   s      r   convert_ByteStringPropertyr(             r   c                 $    t          j        di |S )z2Returns a form field for a ``db.BooleanProperty``.r   )r   BooleanFieldr"   s      r   convert_BooleanPropertyr,      s    >##F###r   c                      t          |          S )z2Returns a form field for a ``db.IntegerProperty``.)r   r"   s      r   convert_IntegerPropertyr.      s    F###r   c                 $    t          j        di |S )z0Returns a form field for a ``db.FloatProperty``.r   )r   
FloatFieldr"   s      r   convert_FloatPropertyr1      s    <!!&!!!r   c                 p    |j         s|j        rdS |                    dd           t          j        di |S )z3Returns a form field for a ``db.DateTimeProperty``.Nformatz%Y-%m-%d %H:%M:%Sr   auto_nowauto_now_add
setdefaultr   DateTimeFieldr"   s      r   convert_DateTimePropertyr9      sH    } ) t
h 3444?$$V$$$r   c                 p    |j         s|j        rdS |                    dd           t          j        di |S )z/Returns a form field for a ``db.DateProperty``.Nr3   z%Y-%m-%dr   )r5   r6   r7   r   	DateFieldr"   s      r   convert_DatePropertyr<      sG    } ) t
h
+++;     r   c                 p    |j         s|j        rdS |                    dd           t          j        di |S )z/Returns a form field for a ``db.TimeProperty``.Nr3   z%H:%M:%Sr   r4   r"   s      r   convert_TimePropertyr>      sG    } ) t
h
+++?$$V$$$r   c                     dS )z/Returns a form field for a ``db.ListProperty``.Nr   r"   s      r   convert_ListPropertyr@          4r   c                     t          di |S )z5Returns a form field for a ``db.StringListProperty``.r   )r
   r"   s      r   convert_StringListPropertyrC      s    ",,V,,,r   c                 f    |j         |d<   |                    d|j                    t          di |S )z4Returns a form field for a ``db.ReferenceProperty``.reference_classallow_blankr   )rE   r7   requiredr	   r"   s      r   convert_ReferencePropertyrH      s?     $ 4F
m%6777!++F+++r   c                     dS )z8Returns a form field for a ``db.SelfReferenceProperty``.Nr   r"   s      r   convert_SelfReferencePropertyrJ      rA   r   c                     dS )z/Returns a form field for a ``db.UserProperty``.Nr   r"   s      r   convert_UserPropertyrL      rA   r   c                 $    t          j        di |S )z/Returns a form field for a ``db.BlobProperty``.r   )r   	FileFieldr"   s      r   convert_BlobPropertyrO      s    ;     r   c                 $    t          j        di |S )z/Returns a form field for a ``db.TextProperty``.r   )r   r!   r"   s      r   convert_TextPropertyrQ      s    ?$$V$$$r   c                      t          |          S )z3Returns a form field for a ``db.CategoryProperty``.r'   r"   s      r   convert_CategoryPropertyrS      r)   r   c                 x    |d                              t          j                               t          |          S )z/Returns a form field for a ``db.LinkProperty``.r   )r   r   urlr   r"   s      r   convert_LinkPropertyrV      s1    
<
 0 0111   r   c                 x    |d                              t          j                               t          |          S )z0Returns a form field for a ``db.EmailProperty``.r   )r   r   emailr   r"   s      r   convert_EmailPropertyrY      s2    
<
 0 2 2333   r   c                     t          di |S )z0Returns a form field for a ``db.GeoPtProperty``.r   )r   r"   s      r   convert_GeoPtPropertyr[      s    '''''r   c                     dS )z-Returns a form field for a ``db.IMProperty``.Nr   r"   s      r   convert_IMPropertyr]      rA   r   c                      t          |          S )z6Returns a form field for a ``db.PhoneNumberProperty``.r'   r"   s      r   convert_PhoneNumberPropertyr_      r)   r   c                      t          |          S )z8Returns a form field for a ``db.PostalAddressProperty``.r'   r"   s      r   convert_PostalAddressPropertyra      r)   r   c                     |d                              t          j        dd                     t          j        di |S )z1Returns a form field for a ``db.RatingProperty``.r   r   d   r   r   )r   r   r   r   r   r"   s      r   convert_RatingPropertyrd      sA    
<
 61# F F FGGG>##F###r   c                       e Zd ZdZi dededededede	de
d	ed
ededededededededeeeeeeedZ eg d          ZddZd ZdS )ModelConvertera  
    Converts properties from a ``db.Model`` class to form fields.

    Default conversions between properties and fields:

    +====================+===================+==============+==================+
    | Property subclass  | Field subclass    | datatype     | notes            |
    +====================+===================+==============+==================+
    | StringProperty     | TextField         | unicode      | TextArea         |
    |                    |                   |              | if multiline     |
    +--------------------+-------------------+--------------+------------------+
    | ByteStringProperty | TextField         | str          |                  |
    +--------------------+-------------------+--------------+------------------+
    | BooleanProperty    | BooleanField      | bool         |                  |
    +--------------------+-------------------+--------------+------------------+
    | IntegerProperty    | IntegerField      | int or long  |                  |
    +--------------------+-------------------+--------------+------------------+
    | 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]   |
    +--------------------+-------------------+--------------+------------------+
    | ListProperty       | None              | list         | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | StringListProperty | TextAreaField     | list of str  |                  |
    +--------------------+-------------------+--------------+------------------+
    | ReferenceProperty  | ReferencePropertyF| db.Model     |                  |
    +--------------------+-------------------+--------------+------------------+
    | SelfReferenceP.    | ReferencePropertyF| db.Model     |                  |
    +--------------------+-------------------+--------------+------------------+
    | UserProperty       | None              | users.User   | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | BlobProperty       | FileField         | str          |                  |
    +--------------------+-------------------+--------------+------------------+
    | TextProperty       | TextAreaField     | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | CategoryProperty   | TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | LinkProperty       | TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | EmailProperty      | TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | GeoPtProperty      | TextField         | db.GeoPt     |                  |
    +--------------------+-------------------+--------------+------------------+
    | IMProperty         | None              | db.IM        | always skipped   |
    +--------------------+-------------------+--------------+------------------+
    | PhoneNumberProperty| TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | PostalAddressP.    | TextField         | unicode      |                  |
    +--------------------+-------------------+--------------+------------------+
    | RatingProperty     | IntegerField      | int or long  |                  |
    +--------------------+-------------------+--------------+------------------+
    | _ReverseReferenceP.| None              | <iterable>   | always skipped   |
    +====================+===================+==============+==================+
    StringPropertyByteStringPropertyBooleanPropertyIntegerPropertyFloatPropertyDateTimePropertyDatePropertyTimePropertyListPropertyStringListPropertyReferencePropertySelfReferencePropertyUserPropertyBlobPropertyTextPropertyCategoryPropertyLinkProperty)EmailPropertyGeoPtProperty
IMPropertyPhoneNumberPropertyPostalAddressPropertyRatingProperty)ro   rp   ri   Nc                 "    |p| j         | _        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).
        N)default_converters
converters)selfr   s     r   __init__zModelConverter.__init__Y  s     %?(?r   c                    t          |          j        }|j                            dd                                          |                                g d}|r|                    |           |j        r5|| j        vr,|d         	                    t          j                               |j        r)d|vrd |j        D             |d<   t          j        di |S | j                            |d          }| ||||          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.
        _ )labeldefaultr   r   choicesc                     g | ]}||fS r   r   ).0r   s     r   
<listcomp>z*ModelConverter.convert.<locals>.<listcomp>}  s    $B$B$BaV$B$B$Br   Nr   )type__name__namereplacetitledefault_valueupdaterG   NO_AUTO_REQUIREDr   r   r   r   SelectFieldr   get)r   r#   r$   
field_argsprop_type_namer   	converters          r   convertzModelConverter.convertc  s$    d,Y&&sC006688))++
 

  	&MM*%%%= 	?^43HHH< ''
(;(=(=>>>< 	6&&$B$BT\$B$B$By!=**6***++NDAAI$ yf555 %$r   N) r   
__module____qualname____doc__r%   r(   r,   r.   r1   r9   r<   r>   r@   rC   rH   rJ   rL   rO   rQ   rS   rV   rY   r[   r]   r_   ra   rd   r   	frozensetr   r   r   r   r   r   rf   rf      sc       < <z!7!; 	!8 	!8	
 	!6 	!9 	!5 	!5 	!5 	!; 	!: 	 !> 	!5 	!5 	!5  	!9!" 	!5#$ "7!6!3!<!>!7/  6 !y!Z!Z!Z[[@ @ @ @6 6 6 6 6r   rf   Nc                   
 |pt                      }|pi }|                                 }t          t          |          d           }t	          d |D                       
|rt	          
fd|D                       
nrt	          fd
D                       
i }
D ]9}|                    | ||         |                    |                    }	|	|	||<   :|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                     | d         j         S )N   )creation_counter)r$   s    r   <lambda>zmodel_fields.<locals>.<lambda>  s    T!W=U r   )keyc              3   &   K   | ]}|d          V  dS )r   Nr   )r   xs     r   	<genexpr>zmodel_fields.<locals>.<genexpr>  s&      22qt222222r   c              3   $   K   | ]
}|v |V  d S r   r   )r   r   field_namess     r   r   zmodel_fields.<locals>.<genexpr>  s-      ??a;.>.>1.>.>.>.>??r   c              3   $   K   | ]
}|v|V  d S r   r   )r   r   excludes     r   r   zmodel_fields.<locals>.<genexpr>  s-      FFQg5E5E15E5E5E5EFFr   )rf   
propertiessortedr   listr   r   )r#   onlyr   r   r   propssorted_props
field_dictr   fieldr   s     `       @r   model_fieldsr     s   * -^--I!rJ E)E**0U0UVVVL22\22222K G????d?????	 GFFFFkFFFFF J % %!!%tjnnT6J6JKK$Jtr   c                 v    t          | ||||          }t          |                                 dz   |f|          S )a,  
    Creates and returns a dynamic ``wtforms.Form`` class for a given
    ``db.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 ``db.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   )r   r   kind)r#   
base_classr   r   r   r   r   s          r   
model_formr     s<    4 eT7J	JJJ 

v%
}jAAAr   )NNNN)*r   wtformsr   r   r   r   r   wtforms.compatr   wtforms.ext.appengine.fieldsr   r	   r
   r   r   r%   r(   r,   r.   r1   r9   r<   r>   r@   rC   rH   rJ   rL   rO   rQ   rS   rV   rY   r[   r]   r_   ra   rd   objectrf   r   r   r   r   r   <module>r      sb  \ \z ; : : : : : : : : : : : $ $ $ $ $ $ l l l l l l l l l l! ! !$ $ $% % %! ! !
$ $ $
$ $ $
" " "
% % %! ! !% % %  
- - -
, , ,  
  
! ! !
% % %
! ! !
! ! !! ! !( ( (
  
! ! !
! ! !
$ $ $D6 D6 D6 D6 D6V D6 D6 D6N =A* * * *Z "&D$4B B B B B Br   