Exemplo n.º 1
0
class _IncomeSearchSchema(ResourceSearchSchema):
    invoice_id = colander.SchemaNode(
        colander.Integer(),
        missing=None
    )
    account_id = colander.SchemaNode(
        colander.Integer(),
        missing=None
    )
    currency_id = colander.SchemaNode(
        colander.String(),
        missing=None,
        validator=currency_validator,
    )
    payment_from = colander.SchemaNode(
        Date(),
        missing=None
    )
    payment_to = colander.SchemaNode(
        Date(),
        missing=None
    )
    sum_from = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
    sum_to = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
Exemplo n.º 2
0
class _CrosspaymentSearchSchema(ResourceSearchSchema):
    subaccount_from_id = colander.SchemaNode(
        colander.Integer(),
        missing=None,
    )
    subaccount_to_id = colander.SchemaNode(
        colander.Integer(),
        missing=None,
    )
    account_item_id = colander.SchemaNode(
        colander.Integer(),
        missing=None,
    )
    date_from = colander.SchemaNode(
        Date(),
        missing=None
    )
    date_to = colander.SchemaNode(
        Date(),
        missing=None
    )
    sum_from = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
    sum_to = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
Exemplo n.º 3
0
class UpdateRecurringSchema(colander.MappingSchema):
    name = colander.SchemaNode(colander.String(),
                               validator=colander.Length(max=60),
                               missing=colander.drop)
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(0, 20000),
                                 required=True)
    start_date = colander.SchemaNode(colander.Date(),
                                     missing=today)
    trial_amount = colander.SchemaNode(colander.Decimal('0.01'),
                                       validator=colander.Range(0, 20000),
                                       missing=colander.drop)
    total_occurrences = colander.SchemaNode(colander.Integer(),
                                            validator=colander.Range(1, 9999),
                                            missing=9999)
    trial_occurrences = colander.SchemaNode(colander.Integer(),
                                            validator=colander.Range(1, 99),
                                            missing=colander.drop)

    credit_card = CreditCardSchema(validator=CreditCardSchema.validator,
                                   missing=colander.drop)
    bank_account = BankAccountSchema(validator=BankAccountSchema.validator,
                                     missing=colander.drop)
    customer = CustomerBaseSchema(missing=colander.drop)
    order = OrderSchema(missing=colander.drop)
    billing = AddressSchema(missing=colander.drop)
    shipping = AddressSchema(missing=colander.drop)
Exemplo n.º 4
0
class WithdrawSchema(CSRFSchema):

    address = colander.SchemaNode(
        colander.String(),
        title="To address",
        validator=validate_ethereum_address,
        widget=deform.widget.TextInputWidget(size=6, maxlength=6, template="textinput_placeholder", placeholder="0x0000000000000000000000000000000000000000")
    )

    amount = colander.SchemaNode(
        colander.Decimal(),
        validator=validate_withdraw_amount,
        widget=deform.widget.TextInputWidget(size=6, maxlength=6, template="textinput_placeholder", placeholder="0.00")
        )

    note = colander.SchemaNode(
        colander.String(),
        title="Note",
        missing="",
        description="The note is recorded for your own transaction history.")

    advanced = Advanced(
        title="Advanced",
        widget=deform.widget.MappingWidget(
            template="mapping_accordion",
            open=False))
Exemplo n.º 5
0
class TimeEntrySchema(colander.Schema):
    title = colander.SchemaNode(
        colander.String(),
        title=_("Title"),
        missing="",
    )
    tariff_uid = colander.SchemaNode(
        colander.String(),
        title=_("Tariff"),
        missing="",
        widget=selectable_tariff_radio,
    )
    created = colander.SchemaNode(
        LocalDateTime(),
        missing=None,
        widget=deform.widget.DateTimeInputWidget(time_options={'interval': 5}),
        title=_("Start time"))
    stop_time = colander.SchemaNode(
        LocalDateTime(),
        missing=None,
        widget=deform.widget.DateTimeInputWidget(time_options={'interval': 5}),
        title=_("End time"))
    bill_hours = colander.SchemaNode(colander.Decimal(),
                                     missing=None,
                                     title=_("Bill hours"))
Exemplo n.º 6
0
def getNode(_type,tr,config):
    from bips.workflows.flexible_datagrabber import Data, DataBase
    if _type == type(traits.Int()):
            col_type = colander.SchemaNode(colander.Int(),
                                           name=tr,description=config.trait(tr).desc)
    elif _type == type(traits.Float()):
        col_type = colander.SchemaNode(colander.Decimal(),name=tr)    
        
    elif _type == type(traits.String()) or _type==type(traits.Str()):
        col_type = colander.SchemaNode(colander.String(),name=tr)
        
    elif _type == type(traits.Enum('')):
        values=config.trait(tr).trait_type.values
        the_values = []
        for v in values:
            the_values.append((v,v))
        col_type = colander.SchemaNode(
            deform.Set(),
            widget=deform.widget.SelectWidget(values=the_values),
            name=tr)
    elif _type == type(traits.Bool()):
        col_type = colander.SchemaNode(colander.Boolean(),widget=deform.widget.CheckboxWidget(),name=tr)
    elif _type == type(traits.Code()):
        col_type = colander.SchemaNode(colander.String(),name=tr,widget=deform.widget.TextAreaWidget(cols=100,rows=20))
    elif _type == type(traits.Instance(Data,())):
        from bips.workflows.flexible_datagrabber import create_datagrabber_html_view
        col_type = create_datagrabber_html_view() 
    elif _type == type(traits.List()):
        col_type =get_list(_type,tr,config) 
    else:
        print "type: ", _type, "not found!"
        col_type = colander.SchemaNode(colander.String(),name=tr)
    return col_type
Exemplo n.º 7
0
    class Fees(colander.Schema):
        member_type = colander.SchemaNode(
            colander.String(),
            title=_(u'Please tell us wether you\'re an individual, '
                    u'freelancer or company or want to support us '
                    u'generously as a sustaining member'),
            widget=deform.widget.RadioChoiceWidget(
                values=[(member_type, t_description) for fee, member_type,
                        t_description in customization.membership_fees]),
            oid='member_type')

        # not validating here: depends on ^
        # http://deformdemo.repoze.org/require_one_or_another/
        member_custom_fee = colander.SchemaNode(
            colander.Decimal('1.00'),
            title=_(u'custom membership fee'),
            widget=deform.widget.MoneyInputWidget(
                symbol=customization.currency,
                showSymbol=True,
                defaultZero=True),
            description=_(
                u'Sustaining members: You can set your fees (minimum 100 €)'),
            oid='membership_custom_fee',
            default=customization.membership_fee_custom_min,
            validator=Range(
                min=customization.membership_fee_custom_min,
                max=None,
                min_err=
                _(u'please enter at least the minimum fee for sustaining members'
                  )))
Exemplo n.º 8
0
class _VatSchema(ResourceSchema):
    date = colander.SchemaNode(
        Date(),
        validator=date_validator,
    )
    account_id = colander.SchemaNode(
        colander.String(),
        validator=account_validator
    )
    service_id = colander.SchemaNode(
        colander.String(),
        validator=service_validator
    )
    vat = colander.SchemaNode(
        colander.Decimal('.01'),
        validator=colander.Range(min=0, max=100),
    )
    calc_method = colander.SchemaNode(
        colander.String()
    )
    descr = colander.SchemaNode(
        colander.String(),
        validator=colander.Length(max=255),
        missing=None
    )
Exemplo n.º 9
0
 def colander_literal_type(self, data_input):
     # LOGGER.debug('data input type = %s', data_input.dataType)
     if 'boolean' in data_input.dataType:
         return colander.Boolean()
     elif 'integer' in data_input.dataType:
         return colander.Integer()
     elif 'float' in data_input.dataType:
         return colander.Float()
     elif 'double' in data_input.dataType:
         return colander.Float()
     elif 'decimal' in data_input.dataType:
         return colander.Decimal()
     elif 'string' in data_input.dataType:
         return colander.String()
     elif 'dateTime' in data_input.dataType:
         return colander.DateTime()
     elif 'date' in data_input.dataType:
         return colander.Date()
     elif 'time' in data_input.dataType:
         return colander.Time()
     elif 'duration' in data_input.dataType:
         # TODO: check correct type
         # http://www.w3.org/TR/xmlschema-2/#duration
         return colander.Time()
     # guessing from default
     elif hasattr(data_input, 'defaultValue'):
         try:
             dateutil.parser.parse(data_input.defaultValue)
         except Exception:
             return colander.String()
         else:
             return colander.DateTime()
     else:
         return colander.String()
Exemplo n.º 10
0
class _OutgoingSearchSchema(ResourceSearchSchema):
    account_id = colander.SchemaNode(
        colander.Integer(),
        missing=None
    )
    account_item_id = colander.SchemaNode(
        colander.Integer(),
        missing=None
    )
    sum_from = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
    sum_to = colander.SchemaNode(
        colander.Decimal(),
        missing=None
    )
Exemplo n.º 11
0
class _CommissionSchema(ResourceSchema):
    service_id = colander.SchemaNode(SelectInteger(Service), )
    percentage = colander.SchemaNode(colander.Decimal(),
                                     validator=colander.Range(min=0, max=100))
    price = colander.SchemaNode(colander.Money(), )
    currency_id = colander.SchemaNode(SelectInteger(Currency), )
    descr = colander.SchemaNode(colander.String(),
                                validator=colander.Length(max=255),
                                missing=u'')
Exemplo n.º 12
0
class SubscriptionSchema(Schema):

    subscription_type = colander.SchemaNode(colander.String(),
                                            widget=subscription_type_widget,
                                            title=_('Subscription type'))

    price = colander.SchemaNode(colander.Decimal(),
                                widget=deform.widget.MoneyInputWidget(
                                    size=20, options={'allowZero': True}),
                                title=_('Price'))
Exemplo n.º 13
0
class AmountItemSchema(colander.MappingSchema):
    name = colander.SchemaNode(colander.String(),
                               validator=colander.Length(max=31),
                               missing=colander.drop)
    description = colander.SchemaNode(colander.String(),
                                      validator=colander.Length(max=255),
                                      missing=colander.drop)
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(min=0),
                                 missing=colander.drop)
Exemplo n.º 14
0
class RefundTransactionSchema(colander.MappingSchema):
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(0, 20000),
                                 required=True)
    transaction_id = colander.SchemaNode(colander.String(),
                                         validator=colander.Length(max=60),
                                         required=True)
    last_four = colander.SchemaNode(colander.String(),
                                    validator=colander.Length(max=16),
                                    required=True)
Exemplo n.º 15
0
class TariffSchema(colander.Schema):
    title = colander.SchemaNode(colander.String(), title=_("Title"))
    price = colander.SchemaNode(colander.Decimal(),
                                title=_("Price"),
                                default=0)
    currency = colander.SchemaNode(colander.String(),
                                   title=_("Currency"),
                                   missing="")
    vat = colander.SchemaNode(colander.Int(),
                              title=_("VAT in integer percentage"),
                              default=0)
Exemplo n.º 16
0
class TransactionBaseSchema(colander.MappingSchema):
    line_items = LineItemsSchema(validator=colander.Length(max=30),
                                 missing=colander.drop)
    order = OrderSchema(missing=colander.drop)
    tax = AmountItemSchema(missing=colander.drop)
    duty = AmountItemSchema(missing=colander.drop)
    shipping_and_handling = AmountItemSchema(missing=colander.drop)
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(0, 20000),
                                 required=True)
    split_tender_id = colander.SchemaNode(colander.String(),
                                          missing=colander.drop)
    tax_exempt = colander.SchemaNode(colander.Boolean(), missing=colander.drop)
Exemplo n.º 17
0
class CreateRecurringSchema(UpdateRecurringSchema):
    interval_length = colander.SchemaNode(colander.Integer(),
                                          validator=colander.Range(1, 999),
                                          required=True)
    interval_unit = colander.SchemaNode(colander.String(),
                                        validator=colander.OneOf(['days', 'months']),
                                        required=True)
    start_date = colander.SchemaNode(colander.Date(),
                                     missing=today)
    total_occurrences = colander.SchemaNode(colander.Integer(),
                                            validator=colander.Range(1, 9999),
                                            missing=9999)
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(0, 20000),
                                 required=True)
Exemplo n.º 18
0
class LineItemSchema(colander.MappingSchema):
    item_id = colander.SchemaNode(colander.String(),
                                  validator=colander.Length(max=31),
                                  missing=colander.drop)
    name = colander.SchemaNode(colander.String(),
                               validator=colander.Length(max=31),
                               missing=colander.drop)
    description = colander.SchemaNode(colander.String(),
                                      validator=colander.Length(max=255),
                                      missing=colander.drop)
    quantity = colander.SchemaNode(colander.Integer(),
                                   validator=colander.Range(min=0, max=99),
                                   missing=colander.drop)
    unit_price = colander.SchemaNode(colander.Decimal('0.01'),
                                     validator=colander.Range(min=0),
                                     missing=colander.drop)
    taxable = colander.SchemaNode(colander.Boolean(), missing=colander.drop)
Exemplo n.º 19
0
 def colander_literal_type(self, data_input):
     # LOGGER.debug('data input type = %s', data_input.dataType)
     if 'boolean' in data_input.dataType:
         return colander.Boolean()
     elif 'integer' in data_input.dataType:
         return colander.Integer()
     elif 'float' in data_input.dataType:
         return colander.Float()
     elif 'double' in data_input.dataType:
         return colander.Float()
     elif 'angle' in data_input.dataType:
         return colander.Float()
     elif 'decimal' in data_input.dataType:
         return colander.Decimal()
     elif 'string' in data_input.dataType:
         if data_input.maxOccurs > 1:
             # we are going to use a SelectWidget with multiple=True
             return colander.Set()
         elif data_input.identifier.endswith("FileUpload"):
             # we want to upload a file but just return a string containing
             # the path
             return deform.FileData()
         else:
             return colander.String()
     elif 'dateTime' in data_input.dataType:
         return colander.DateTime()
     elif 'date' in data_input.dataType:
         return colander.Date()
     elif 'time' in data_input.dataType:
         return colander.Time()
     elif 'duration' in data_input.dataType:
         # TODO: check correct type
         # http://www.w3.org/TR/xmlschema-2/#duration
         return colander.Time()
     # guessing from default
     elif hasattr(data_input, 'defaultValue'):
         try:
             dateutil.parser.parse(data_input.defaultValue)
         except Exception:
             return colander.String()
         else:
             return colander.DateTime()
     else:
         return colander.String()
Exemplo n.º 20
0
class Transfer(colander.MappingSchema):
    donor = colander.SchemaNode(
        name='donor',
        typ=colander.Int(),
        validator=colander.Range(min=0),
        missing=colander.required,
        description=u'Id account - уникальный индификатор донора')

    recipient = colander.SchemaNode(
        name='recipient',
        typ=colander.Int(),
        validator=colander.Range(min=0),
        missing=colander.required,
        description=u'Id account - уникальный индификатор получателя')
    delta = colander.SchemaNode(name='delta',
                                typ=colander.Decimal(),
                                validator=colander.Range(min=0),
                                missing=colander.required,
                                description=u'Сумма перевода')
Exemplo n.º 21
0
class TransactionBaseSchema(colander.MappingSchema):
    line_items = LineItemsSchema(validator=colander.Length(max=30),
                                 missing=colander.drop)
    user_fields = UserFieldsSchema(validator=colander.Length(max=30),
                                   missing=colander.drop)
    order = OrderSchema(missing=colander.drop)
    tax = AmountItemSchema(missing=colander.drop)
    duty = AmountItemSchema(missing=colander.drop)
    shipping_and_handling = AmountItemSchema(missing=colander.drop)
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(min=0),
                                 required=True)
    currency_code = colander.SchemaNode(colander.String(),
                                        missing=colander.drop)
    split_tender_id = colander.SchemaNode(colander.String(),
                                          missing=colander.drop)
    tax_exempt = colander.SchemaNode(colander.Boolean(), missing=colander.drop)
    customer_ip = colander.SchemaNode(colander.String(),
                                      validator=colander.Length(max=39),
                                      missing=colander.drop)
    transaction_settings = TransactionSettings(missing=colander.drop)
Exemplo n.º 22
0
class ClientItemSchema(colander.Schema):
    client_item_id = colander.SchemaNode(colander.Integer(),
                                         widget=HiddenWidget(),
                                         validator=client_item_id_validator,
                                         missing_msg=REQUIRED_FIELD)
    name = colander.SchemaNode(
        colander.String(),
        title=Item.NAME,
        widget=TextInputWidget(
            readonly=True, readonly_template=u'readonly/textinput_readonly'),
        missing_msg=REQUIRED_FIELD)
    price = colander.SchemaNode(
        colander.Decimal('0.01', decimal.ROUND_HALF_EVEN),
        title=Item.PRICE,
        missing_msg=REQUIRED_FIELD,
        widget=TextInputWidget(),
        validator=colander.Range(
            min=0,
            max=999999.99,
            min_err=string_constants.MIN_NUMBER_RANGE_ERROR.format(0),
            max_err=string_constants.MAX_NUMBER_RANGE_ERROR.format(999999.99)))
Exemplo n.º 23
0
    def get_schema_from_col(self, column, nullable=None):
        """ Build and return a Colander SchemaNode
            using information stored in the column.
        """

        if hasattr(column, 'ca_registry'):
            params = column.ca_registry.copy()

        else:
            params = {}

        # support sqlalchemy.types.TypeDecorator
        column_type = getattr(column.type, 'impl', column.type)

        if 'type' in params:
            type_ = params.pop('type')

        elif isinstance(column_type, sqlalchemy.types.Boolean):
            type_ = colander.Boolean()

        elif isinstance(column_type, sqlalchemy.types.Date):
            type_ = colander.Date()

        elif isinstance(column_type, sqlalchemy.types.DateTime):
            type_ = colander.DateTime()

        elif isinstance(column_type, sqlalchemy.types.Enum):
            type_ = colander.String()

        elif isinstance(column_type, sqlalchemy.types.Float):
            type_ = colander.Float()

        elif isinstance(column_type, sqlalchemy.types.Integer):
            type_ = colander.Integer()

        elif isinstance(column_type, sqlalchemy.types.String):
            type_ = colander.String()

        elif isinstance(column_type, sqlalchemy.types.Numeric):
            type_ = colander.Decimal()

        elif isinstance(column_type, sqlalchemy.types.Time):
            type_ = colander.Time()

        else:
            raise NotImplementedError('Unknown type: %s' % column.type)

        if 'children' in params:
            children = params.pop('children')
        else:
            children = []

        # Add a default value for missing parameters during serialization.
        if 'default' not in params and column.default is None:
            params['default'] = colander.null

        elif 'default' not in params and not column.default is None:
            params['default'] = column.default.arg

        # Add a default value for  missing parameters during deserialization.
        if 'missing' not in params and not column.nullable:
            params['missing'] = colander.required

        elif 'missing' not in params and column.default is None:
            params['missing'] = None

        elif 'missing' not in params and not column.default is None:
            params['missing'] = column.default.arg

        # Overwrite default missing value when nullable is specified.
        if nullable is False:
            params['missing'] = colander.required

        elif nullable is True:
            params['missing'] = None

        if 'validator' not in params and \
           isinstance(column_type, sqlalchemy.types.Enum):

            params['validator'] = colander.OneOf(column.type.enums)

        elif 'validator' not in params and \
           isinstance(column_type, sqlalchemy.types.Enum):

            params['validator'] = colander.Length(0, column.type.length)

        if 'name' not in params:
            params['name'] = column.name

        return colander.SchemaNode(type_, *children, **params)
Exemplo n.º 24
0
def decimal_property(**kwargs):
    return colander.SchemaNode(colander.Decimal(), **kwargs)
Exemplo n.º 25
0
class AssetSchema(colander.Schema):

    #: Human readable name
    name = colander.SchemaNode(colander.String())

    #:  The network this asset is in
    network = colander.SchemaNode(
        UUIDForeignKeyValue(model=AssetNetwork, match_column="id"),
        widget=defer_widget_values(deform.widget.SelectWidget,
                                   available_networks),
    )

    #: Symbol how this asset is presented in tickers
    symbol = colander.SchemaNode(colander.String())

    description = colander.SchemaNode(colander.String(), missing="")

    #: Markdown page telling about this asset
    long_description = colander.SchemaNode(colander.String(),
                                           description="Markdown formatted",
                                           missing="",
                                           widget=deform.widget.TextAreaWidget(
                                               rows=20, cols=80))

    #: Ethereum address
    external_id = colander.SchemaNode(colander.String(),
                                      title="Address",
                                      validator=validate_ethereum_address,
                                      missing=None,
                                      description="0x hex string format")

    #: Number of units avaialble
    supply = colander.SchemaNode(colander.Decimal(), missing=None)

    #: What kind of asset is this
    asset_class = colander.SchemaNode(
        EnumValue(AssetClass),
        widget=deform.widget.SelectWidget(values=enum_values(AssetClass)))

    #: Workflow state of this asset
    state = colander.SchemaNode(
        EnumValue(AssetState),
        widget=deform.widget.SelectWidget(values=enum_values(AssetState)))

    other_data = colander.SchemaNode(
        JSONValue(),
        widget=JSONWidget(),
        description="JSON bag of attributes of the object",
        missing=dict)

    def dictify(self, obj: Asset) -> dict:
        """Serialize SQLAlchemy model instance to nested dictionary appstruct presentation."""

        appstruct = dictify(self,
                            obj,
                            excludes=("long_description", "external_id"))

        # Convert between binary storage and human readable hex presentation
        appstruct["long_description"] = obj.other_data.pop(
            "long_description", "")

        if obj.external_id:
            appstruct["external_id"] = bin_to_eth_address(obj.external_id)
        else:
            appstruct["external_id"] = ""

        return appstruct

    def objectify(self, appstruct: dict, obj: Asset):
        """Store the dictionary data from the form submission on the object."""

        objectify(self,
                  appstruct,
                  obj,
                  excludes=("long_description", "external_id"))

        if not obj.other_data:
            # When creating the object JSON value may be None
            # instead of empty dict
            obj.other_data = {}

        # Special case of field stored inside JSON bag
        obj.other_data["long_description"] = appstruct["long_description"]

        # Convert between binary storage and human readable hex presentation
        if appstruct["external_id"]:
            obj.external_id = eth_address_to_bin(appstruct["external_id"])
Exemplo n.º 26
0
class CreditTransactionSchema(CIMBaseSchema):
    amount = colander.SchemaNode(colander.Decimal('0.01'),
                                 validator=colander.Range(0, 20000),
                                 required=True)
Exemplo n.º 27
0
    def get_schema_from_column(self, prop, overrides):
        """Extended to handle JSON/JSONB.
        """

        # The name of the SchemaNode is the ColumnProperty key.
        name = prop.key
        kwargs = dict(name=name)
        column = prop.columns[0]

        typedecorator_overrides = getattr(column.type, self.ca_class_key,
                                          {}).copy()
        # print("colanderalchemy ", prop, typedecorator_overrides)

        declarative_overrides = column.info.get(self.sqla_info_key, {}).copy()
        self.declarative_overrides[name] = declarative_overrides.copy()

        key = 'exclude'

        if key not in itertools.chain(declarative_overrides, overrides) \
           and typedecorator_overrides.pop(key, False):
            log.debug('Column %s skipped due to TypeDecorator overrides', name)
            return None

        if key not in overrides and declarative_overrides.pop(key, False):
            log.debug('Column %s skipped due to declarative overrides', name)
            return None

        if overrides.pop(key, False):
            log.debug('Column %s skipped due to imperative overrides', name)
            return None

        self.check_overrides(name, 'name', typedecorator_overrides,
                             declarative_overrides, overrides)

        for key in ['missing', 'default']:
            self.check_overrides(name, key, typedecorator_overrides, {}, {})

        # The SchemaNode built using the ColumnProperty has no children.
        children = []

        # The type of the SchemaNode will be evaluated using the Column type.
        # User can overridden the default type via Column.info or
        # imperatively using overrides arg in SQLAlchemySchemaNode.__init__

        # Support sqlalchemy.types.TypeDecorator

        dialect = self.dbsession.bind.engine.dialect
        column_type = column.type.dialect_impl(dialect)

        imperative_type = overrides.pop('typ', None)
        declarative_type = declarative_overrides.pop('typ', None)
        typedecorator_type = typedecorator_overrides.pop('typ', None)

        if self.type_overrides is not None:

            type_overrides_type, type_overrides_kwargs = self.type_overrides(
                self, name, column, column_type)

            if type_overrides_type == TypeOverridesHandling.drop:
                # This column should not appear on the form
                log.debug('Column %s: dropped by type overrides callback',
                          name)
                return None

            elif type_overrides_type == TypeOverridesHandling.unknown:
                # type overrides callback doesn't know about this column
                type_overrides_type = None
                type_overrides_kwargs = {}

        else:
            type_overrides_type = None
            type_overrides_kwargs = {}

        if imperative_type is not None:
            if hasattr(imperative_type, '__call__'):
                type_ = imperative_type()
            else:
                type_ = imperative_type
            log.debug('Column %s: type overridden imperatively: %s.', name,
                      type_)

        elif declarative_type is not None:
            if hasattr(declarative_type, '__call__'):
                type_ = declarative_type()
            else:
                type_ = declarative_type
            log.debug('Column %s: type overridden via declarative: %s.', name,
                      type_)

        elif typedecorator_type is not None:
            if hasattr(typedecorator_type, '__call__'):
                type_ = typedecorator_type()
            else:
                type_ = typedecorator_type
            log.debug('Column %s: type overridden via TypeDecorator: %s.',
                      name, type_)

        elif type_overrides_type is not None:
            if hasattr(type_overrides_type, '__call__'):
                type_ = type_overrides_type()
            else:
                type_ = type_overrides_type
            log.debug('Column %s: type overridden via type_overrides: %s.',
                      name, type_)

        elif isinstance(column_type, Boolean):
            type_ = colander.Boolean()

        elif isinstance(column_type, Date):
            type_ = colander.Date()

        elif isinstance(column_type, DateTime):
            type_ = colander.DateTime(default_tzinfo=None)

        elif isinstance(column_type, Enum):
            type_ = colander.String()
            kwargs["validator"] = colander.OneOf(column.type.enums)

        elif isinstance(column_type, Float):
            type_ = colander.Float()

        elif isinstance(column_type, Integer):
            type_ = colander.Integer()

        elif isinstance(column_type, String):
            type_ = colander.String()
            kwargs["validator"] = colander.Length(0, column.type.length)

        elif isinstance(column_type, Numeric):
            type_ = colander.Decimal()

        elif isinstance(column_type, Time):
            type_ = colander.Time()
        else:
            raise NotImplementedError(
                'Not able to derive a colander type from sqlalchemy '
                'type: %s  Please explicitly provide a colander '
                '`typ` for the "%s" Column.' % (repr(column_type), name))
        """
        Add default values

        possible values for default in SQLA:
         1. plain non-callable Python value
              - give to Colander as a default
         2. SQL expression (derived from ColumnElement)
              - leave default blank and allow SQLA to fill
         3. Python callable with 0 or 1 args
            1 arg version takes ExecutionContext
              - leave default blank and allow SQLA to fill

        all values for server_default should be ignored for
        Colander default
        """
        if (isinstance(column.default, ColumnDefault)
                and column.default.is_scalar):
            kwargs["default"] = column.default.arg
        """
        Add missing values

        possible values for default in SQLA:
         1. plain non-callable Python value
              - give to Colander as a missing unless nullable
         2. SQL expression (derived from ColumnElement)
              - set missing to 'drop' to allow SQLA to fill this in
                and make it an unrequired field
         3. Python callable with 0 or 1 args
            1 arg version takes ExecutionContext
              - set missing to 'drop' to allow SQLA to fill this in
                and make it an unrequired field

        if nullable, then missing = colander.null (this has to be
        the case since some colander types won't accept `None` as
        a value, but all accept `colander.null`)

        all values for server_default should result in 'drop'
        for Colander missing

        autoincrement results in drop
        """
        if isinstance(column.default, ColumnDefault):
            if column.default.is_callable:
                kwargs["missing"] = drop
            elif column.default.is_clause_element:  # SQL expression
                kwargs["missing"] = drop
            elif column.default.is_scalar:
                kwargs["missing"] = column.default.arg
        elif column.nullable:
            kwargs["missing"] = colander.null
        elif isinstance(column.server_default, FetchedValue):
            kwargs["missing"] = drop  # value generated by SQLA backend
        elif (hasattr(column.table, "_autoincrement_column")
              and id(column.table._autoincrement_column) == id(column)):
            # this column is the autoincrement column, so we can drop
            # it if it's missing and let the database generate it
            kwargs["missing"] = drop

        kwargs.update(type_overrides_kwargs)
        kwargs.update(typedecorator_overrides)
        kwargs.update(declarative_overrides)
        kwargs.update(overrides)

        return colander.SchemaNode(type_, *children, **kwargs)
Exemplo n.º 28
0
class Field(object):
    """Represents an individual form field (a visible object in a
    form rendering).

    A :class:`deform.form.Field` object instance is meant to last for
    the duration of a single web request. As a result, a field object
    is often used as a scratchpad by the widget associated with that
    field.  Using a field as a scratchpad makes it possible to build
    implementations of state-retaining widgets while instances of
    those widget still only need to be constructed once instead of on
    each request.

    *Attributes*

        schema
            The schema node associated with this field.

        widget
            The widget associated with this field. When no widget is
            defined in the schema node, a default widget will be created.
            The default widget will have a generated item_css_class
            containing the normalized version of the ``name`` attribute
            (with ``item`` prepended, e.g. ``item-username``).
            NOTE: This behaviour is deprecated and will be removed in
            the future. Mapping and Sequence Widget templates simply
            render a css class on an item's container based on Field
            information.

        order
            An integer indicating the relative order of this field's
            construction to its children and parents.

        oid
            A string incorporating the ``order`` attribute that can be
            used as a unique identifier in HTML code (often for ``id``
            attributes of field-related elements).  A default oid is
            generated that looks like this: ``deformField0``.  A
            custom oid can provided, but if the field is cloned,
            the clones will get unique default oids.

        name
            An alias for self.schema.name

        title
            An alias for self.schema.title

        description
            An alias for self.schema.description

        required
            An alias for self.schema.required

        typ
            An alias for self.schema.typ

        children
            Child fields of this field.

        parent
            The parent field of this field or ``None`` if this field is
            the root.  This is actually a property that returns the result
            of ``weakref.ref(actualparent)()`` to avoid leaks due to circular
            references, but it can be treated like the field itself.

        error
            The exception raised by the last attempted validation of the
            schema element associated with this field.  By default, this
            attribute is ``None``.  If non-None, this attribute is usually
            an instance of the exception class
            :exc:`colander.Invalid`, which has a ``msg`` attribute
            providing a human-readable validation error message.

        errormsg
            The ``msg`` attribute of the ``error`` attached to this field
            or ``None`` if the ``error`` attached to this field is ``None``.

        renderer
            The template :term:`renderer` associated with the form.  If a
            renderer is not passed to the constructor, the default deform
            renderer will be used (the :term:`default renderer`).

        counter
            ``None`` or an instance of ``itertools.counter`` which is used
            to generate sequential order-related attributes such as
            ``oid`` and ``order``.

        resource_registry
            The :term:`resource registry` associated with this field.

        autofocus
            If the field's parent form has its ``focus`` argument set to
            ``on``, the first field out of all fields in this form with
            ``autofocus`` set to a true-ish value (``on``, ``True``, or
            ``autofocus``) will receive focus on page load. Default: ``None``.

    *Constructor Arguments*

      ``renderer``, ``counter``, ``resource_registry`` and ``appstruct`` are
      accepted as explicit keyword arguments to the :class:`deform.Field`.
      These are also available as attribute values.  ``renderer``, if passed,
      is a template renderer as described in :ref:`creating_a_renderer`.
      ``counter``, if passed, should be an :attr:`itertools.counter` object
      (useful when rendering multiple forms on the same page, see
      https://deformdemo.pylonsproject.org/multiple_forms/.
      ``resource_registry``, if passed should be a widget resource registry
      (see also :ref:`get_widget_resources`).

      If any of these values is not passed, a suitable default values is used
      in its place.

      The ``appstruct`` constructor argument is used to prepopulate field
      values related to this form's schema.  If an appstruct is not supplied,
      the form's fields will be rendered with default values unless an
      appstruct is supplied to the ``render`` method explicitly.

      The :class:`deform.Field` constructor also accepts *arbitrary*
      keyword arguments.  When an 'unknown' keyword argument is
      passed, it is attached unmodified to the form field as an
      attribute.

      All keyword arguments (explicit and unknown) are also attached to
      all *children* nodes of the field being constructed.

    """

    error = None
    _cstruct = colander.null
    default_renderer = template.default_renderer
    default_resource_registry = widget.default_resource_registry
    # Allowable input types for automatic focusing
    focusable_input_types = (
        type(colander.Boolean()),
        type(colander.Date()),
        type(colander.DateTime()),
        type(colander.Decimal()),
        type(colander.Float()),
        type(colander.Integer()),
        type(colander.Set()),
        type(colander.String()),
        type(colander.Time()),
    )
    hidden_type = type(HiddenWidget())

    def __init__(self,
                 schema,
                 renderer=None,
                 counter=None,
                 resource_registry=None,
                 appstruct=colander.null,
                 parent=None,
                 autofocus=None,
                 **kw):
        self.counter = counter or itertools.count()
        self.order = next(self.counter)
        self.oid = getattr(schema, "oid", "deformField%s" % self.order)
        self.schema = schema
        self.typ = schema.typ  # required by Invalid exception
        self.name = schema.name
        self.title = schema.title
        self.description = schema.description
        self.required = schema.required
        if renderer is None:
            renderer = self.default_renderer
        if resource_registry is None:
            resource_registry = self.default_resource_registry
        self.renderer = renderer

        # Parameters passed from parent field to child
        if "focus" in kw:
            focus = kw["focus"]
        else:
            focus = "on"
        if "have_first_input" in kw:
            self.have_first_input = kw["have_first_input"]
        else:
            self.have_first_input = False

        if (focus == "off" or autofocus is None or autofocus is False
                or str(autofocus).lower() == "off"):
            self.autofocus = None
        else:
            self.autofocus = "autofocus"

        self.resource_registry = resource_registry
        self.children = []
        if parent is not None:
            parent = weakref.ref(parent)
        self._parent = parent
        self.__dict__.update(kw)

        first_input_index = -1
        child_count = 0
        focused = False
        for child in schema.children:
            if (focus == "on" and not focused
                    and type(child.typ) in Field.focusable_input_types
                    and type(child.widget) != Field.hidden_type
                    and not self.have_first_input):
                first_input_index = child_count
                self.found_first()  # Notify ancestors
            autofocus = getattr(child, "autofocus", None)

            if autofocus is not None:
                focused = True

            kw["have_first_input"] = self.have_first_input
            self.children.append(
                Field(child,
                      renderer=renderer,
                      counter=self.counter,
                      resource_registry=resource_registry,
                      parent=self,
                      autofocus=autofocus,
                      **kw))
            child_count += 1
        if (focus == "on" and not focused and first_input_index != -1
                and self.have_first_input):
            # User did not set autofocus. Focus on first valid input.
            self.children[first_input_index].autofocus = "autofocus"
        self.set_appstruct(appstruct)

    def found_first(self):
        """Set have_first_input of ancestors"""
        self.have_first_input = True
        if self.parent is not None:
            self.parent.found_first()

    @property
    def parent(self):
        if self._parent is None:
            return None
        return self._parent()

    def get_root(self):
        """Return the root field in the field hierarchy (the form field)"""
        node = self
        while True:
            parent = node.parent
            if parent is None:
                break
            node = parent
        return node

    @classmethod
    def set_zpt_renderer(
        cls,
        search_path,
        auto_reload=True,
        debug=True,
        encoding="utf-8",
        translator=None,
    ):
        """Create a :term:`Chameleon` ZPT renderer that will act as a
        :term:`default renderer` for instances of the associated class
        when no ``renderer`` argument is provided to the class'
        constructor.  The arguments to this classmethod have the same
        meaning as the arguments provided to a
        :class:`deform.ZPTRendererFactory`.

        Calling this method resets the :term:`default renderer`.

        This method is effectively a shortcut for
        ``cls.set_default_renderer(ZPTRendererFactory(...))``."""
        cls.default_renderer = template.ZPTRendererFactory(
            search_path,
            auto_reload=auto_reload,
            debug=debug,
            encoding=encoding,
            translator=translator,
        )

    @classmethod
    def set_default_renderer(cls, renderer):
        """Set the callable that will act as a default renderer for
        instances of the associated class when no ``renderer``
        argument is provided to the class' constructor.  Useful when
        you'd like to use an alternate templating system.

        Calling this method resets the :term:`default renderer`.
        """
        cls.default_renderer = staticmethod(renderer)

    @classmethod
    def set_default_resource_registry(cls, registry):
        """Set the callable that will act as a default
        :term:`resource registry` for instances of the associated
        class when no ``resource_registry`` argument is provided to
        the class' constructor.  Useful when you'd like to use
        non-default requirement to resource path mappings for the
        entirety of a process.

        Calling this method resets the default :term:`resource registry`.
        """
        cls.default_resource_registry = registry

    def translate(self, msgid):
        """Use the translator passed to the renderer of this field to
        translate the msgid into a term and return the term.  If the renderer
        does not have a translator, this method will return the msgid."""
        translate = getattr(self.renderer, "translate", None)
        if translate is not None:
            return translate(msgid)
        return msgid

    def __iter__(self):
        """Iterate over the children fields of this field."""
        return iter(self.children)

    def __getitem__(self, name):
        """Return the subfield of this field named ``name`` or raise
        a :exc:`KeyError` if a subfield does not exist named ``name``."""
        for child in self.children:
            if child.name == name:
                return child
        raise KeyError(name)

    def __contains__(self, name):
        for child in self.children:
            if child.name == name:
                return True
        return False

    def clone(self):
        """Clone the field and its subfields, retaining attribute
        information.  Return the cloned field.  The ``order``
        attribute of the node is not cloned; instead the field
        receives a new order attribute; it will be a number larger
        than the last rendered field of this set.  The parent of the cloned
        node will become ``None`` unconditionally."""
        cloned = self.__class__(self.schema)
        cloned.__dict__.update(self.__dict__)
        cloned.order = next(cloned.counter)
        cloned.oid = "deformField%s" % cloned.order
        cloned._parent = None
        children = []
        for field in self.children:
            cloned_child = field.clone()
            cloned_child._parent = weakref.ref(cloned)
            children.append(cloned_child)
        cloned.children = children
        return cloned

    @decorator.reify
    def widget(self):
        """If a widget is not assigned directly to a field, this
        function will be called to generate a default widget (only
        once). The result of this function will then be assigned as
        the ``widget`` attribute of the field for the rest of the
        lifetime of this field. If a widget is assigned to a field
        before form processing, this function will not be called."""
        wdg = getattr(self.schema, "widget", None)
        if wdg is not None:
            return wdg
        widget_maker = getattr(self.schema.typ, "widget_maker", None)
        if widget_maker is None:
            widget_maker = schema.default_widget_makers.get(
                self.schema.typ.__class__)
            if widget_maker is None:
                for (cls, wgt) in schema.default_widget_makers.items():
                    if isinstance(self.schema.typ, cls):
                        widget_maker = wgt
                        break
        if widget_maker is None:
            widget_maker = widget.TextInputWidget
        return widget_maker(item_css_class=self.default_item_css_class())

    def default_item_css_class(self):
        if not self.name:
            return None

        css_class = (unicodedata.normalize("NFKD", compat.text_type(
            self.name)).encode("ascii", "ignore").decode("ascii"))
        css_class = re.sub(r"[^\w\s-]", "", css_class).strip().lower()  # noQA
        css_class = re.sub(r"[-\s]+", "-", css_class)  # noQA
        return "item-%s" % css_class

    def get_widget_requirements(self):
        """Return a sequence of two tuples in the form
        [(``requirement_name``, ``version``), ..].

        The first element in each two-tuple represents a requirement
        name.  When a requirement name is returned as part of
        ``get_widget_requirements``, it means that one or more CSS or
        Javascript resources need to be loaded by the page performing
        the form rendering in order for some widget on the page to
        function properly.

        The second element in each two-tuple is the requested version
        of the library resource.  It may be ``None``, in which case
        the version is unspecified.

        See also the ``requirements`` attribute of
        :class:`deform.Widget` and the explanation of widget
        requirements in :ref:`get_widget_requirements`.
        """
        L = []

        requirements = [req for req in self.widget.requirements] + [
            req for child in self.children
            for req in child.get_widget_requirements()
        ]

        if requirements:
            for requirement in requirements:
                if isinstance(requirement, dict):
                    L.append(requirement)
                else:
                    reqt = tuple(requirement)
                    if reqt not in L:
                        L.append(reqt)
        return L

    def get_widget_resources(self, requirements=None):
        """Return a resources dictionary in the form ``{'js':[seq],
        'css':[seq]}``.  ``js`` represents Javascript resources,
        ``css`` represents CSS resources.  ``seq`` represents a
        sequence of resource paths.  Each path in ``seq`` represents a
        relative resource name, as defined by the mapping of a
        requirement to a set of resource specification by the
        :term:`resource registry` attached to this field or form.

        This method may raise a :exc:`ValueError` if the resource
        registry associated with this field or form cannot resolve a
        requirement to a set of resource paths.

        The ``requirements`` argument represents a set of requirements
        as returned by a manual call to
        :meth:`deform.Field.get_widget_requirements`.  If
        ``requirements`` is not supplied, the requirement are implied
        by calling the :meth:`deform.Field.get_widget_requirements`
        method against this form field.

        See also :ref:`get_widget_resources`.
        """
        if requirements is None:
            requirements = self.get_widget_requirements()
        resources = self.resource_registry(
            (req for req in requirements if not isinstance(req, dict)))
        for req in requirements:
            if not isinstance(req, dict):
                continue
            for key in {'js', 'css'}.intersection(req):
                value = req[key]
                if isinstance(value, str):
                    resources[key].append(value)
                else:
                    resources[key].extend(value)
        return resources

    def set_widgets(self, values, separator="."):
        """set widgets of the child fields of this field
        or form element.  ``widgets`` should be a dictionary in the
        form::

           {'dotted.field.name':Widget(),
            'dotted.field.name2':Widget()}

        The keys of the dictionary are dotted names.  Each dotted name
        refers to a single field in the tree of fields that are
        children of the field or form object upon which this method is
        called.

        The dotted name is split on its dots and the resulting list of
        names is used as a search path into the child fields of this
        field in order to find a field to which to assign the
        associated widget.

        Two special cases exist:

        - If the key is the empty string (``''``), the widget is
          assigned to the field upon which this method is called.

        - If the key contains an asterisk as an element name, the
          first child of the found element is traversed.  This is most
          useful for sequence fields, because the first (and only)
          child of sequence fields is always the prototype field which
          is used to render all fields in the sequence within a form
          rendering.

        If the ``separator`` argument is passed, it is should be a
        string to be used as the dot character when splitting the
        dotted names (useful for supplying if one of your field object
        has a dot in its name, and you need to use a different
        separator).

        Examples follow.  If the following form is used::

          class Person(Schema):
              first_name = SchemaNode(String())
              last_name = SchemaNode(String())

          class People(SequenceSchema):
              person = Person()

          class Conference(Schema):
              people = People()
              name = SchemaNode(String())

          schema = Conference()
          form = Form(schema)

        The following invocations will have the following results
        against the schema defined above:

        ``form.set_widgets({'people.person.first_name':TextAreaWidget()})``

          Set the ``first_name`` field's widget to a ``TextAreaWidget``.

        ``form.set_widgets({'people.*.first_name':TextAreaWidget()})``

          Set the ``first_name`` field's widget to a
          ``TextAreaWidget``.

        ``form.set_widgets({'people':MySequenceWidget()})``

          Set the ``people`` sequence field's widget to a
          ``MySequenceWidget``.

        ``form.set_widgets({'people.*':MySequenceWidget()})``

          Set the *person* field's widget to a ``MySequenceWidget``.

        ``form.set_widgets({'':MyMappingWidget()})``

          Set *form* node's widget to a ``MyMappingWidget``.

        """
        for k, v in values.items():
            if not k:
                self.widget = v
            else:
                path = k.split(separator)
                field = self
                while path:
                    element = path.pop(0)
                    if element == "*":
                        field = field.children[0]
                    else:
                        field = field[element]
                field.widget = v

    @property
    def errormsg(self):
        """Return the ``msg`` attribute of the ``error`` attached to
        this field.  If the ``error`` attribute is ``None``,
        the return value will be ``None``."""
        return getattr(self.error, "msg", None)

    def serialize(self, cstruct=_marker, **kw):
        """Serialize the cstruct into HTML and return the HTML string.  This
        function just turns around and calls ``self.widget.serialize(**kw)``;
        therefore the field widget's ``serialize`` method should be expecting
        any values sent in ``kw``.  If ``cstruct`` is not passed, the cstruct
        attached to this node will be injected into ``kw`` as ``cstruct``.
        If ``field`` is not passed in ``kw``, this field will be injected
        into ``kw`` as ``field``.

        .. note::

           Deform versions before 0.9.8 only accepted a ``readonly``
           keyword argument to this function.  Version 0.9.8 and later accept
           arbitrary keyword arguments.  It also required that
           ``cstruct`` was passed; it's broken out from
           ``kw`` in the method signature for backwards compatibility.
        """
        if cstruct is _marker:
            cstruct = self.cstruct
        values = {"field": self, "cstruct": cstruct}
        values.update(kw)
        return self.widget.serialize(**values)

    def deserialize(self, pstruct):
        """Deserialize the pstruct into a cstruct and return the cstruct."""
        return self.widget.deserialize(self, pstruct)

    def render(self, appstruct=_marker, **kw):
        """Render the field (or form) to HTML using ``appstruct`` as a set
        of default values and returns the HTML string.  ``appstruct`` is
        typically a dictionary of application values matching the schema used
        by this form, or ``colander.null`` to render all defaults.  If it
        is omitted, the rendering will use the ``appstruct`` passed to the
        constructor.

        Calling this method passing an appstruct is the same as calling::

           cstruct = form.set_appstruct(appstruct)
           form.serialize(cstruct, **kw)

        Calling this method without passing an appstruct is the same as
        calling::

           cstruct = form.cstruct
           form.serialize(cstruct, **kw)

        See the documentation for
        :meth:`colander.SchemaNode.serialize` and
        :meth:`deform.widget.Widget.serialize` .

        .. note::

           Deform versions before 0.9.8 only accepted a ``readonly``
           keyword argument to this function.  Version 0.9.8 and later accept
           arbitrary keyword arguments.
        """
        if appstruct is not _marker:
            self.set_appstruct(appstruct)
        cstruct = self.cstruct
        kw.pop("cstruct", None)  # disallowed
        html = self.serialize(cstruct, **kw)
        return html

    def validate(self, controls, subcontrol=None):
        """
        Validate the set of controls returned by a form submission
        against the schema associated with this field or form.
        ``controls`` should be a *document-ordered* sequence of
        two-tuples that represent the form submission data.  Each
        two-tuple should be in the form ``(key, value)``.  ``node``
        should be the schema node associated with this widget.

        For example, using WebOb, you can compute a suitable value for
        ``controls`` via::

          request.POST.items()

        Or, if you're using a ``cgi.FieldStorage`` object named
        ``fs``, you can compute a suitable value for ``controls``
        via::

          controls = []
          if fs.list:
              for control in fs.list:
                  if control.filename:
                      controls.append((control.name, control))
                  else:
                      controls.append((control.name, control.value))

        Equivalent ways of computing ``controls`` should be available to
        any web framework.

        When the ``validate`` method is called:

        - if the fields are successfully validated, a data structure
          represented by the deserialization of the data as per the
          schema is returned.  It will be a mapping.

        - If the fields cannot be successfully validated, a
          :exc:`deform.exception.ValidationFailure` exception is raised.

        The typical usage of ``validate`` in the wild is often
        something like this (at least in terms of code found within
        the body of a :mod:`pyramid` view function, the particulars
        will differ in your web framework)::

          from webob.exc import HTTPFound
          from deform.exception import ValidationFailure
          from deform import Form
          import colander

          from my_application import do_something

          class MySchema(colander.MappingSchema):
              color = colander.SchemaNode(colander.String())

          schema = MySchema()

          def view(request):
              form = Form(schema, buttons=('submit',))
              if 'submit' in request.POST:  # form submission needs validation
                  controls = request.POST.items()
                  try:
                      deserialized = form.validate(controls)
                      do_something(deserialized)
                      return HTTPFound(location='http://example.com/success')
                  except ValidationFailure as e:
                      return {'form':e.render()}
              else:
                  return {'form':form.render()} # the form just needs rendering

        .. warning::

            ``form.validate(controls)`` mutates the ``form`` instance, so the
            ``form`` instance should be constructed (and live) inside one
            request.

        If ``subcontrol`` is supplied, it represents a named subitem in the
        data returned by ``peppercorn.parse(controls)``.  Use this subitem as
        the pstruct to validate instead of using the entire result of
        ``peppercorn.parse(controls)`` as the pstruct to validate.  For
        example, if you've embedded a mapping in the form named ``user``, and
        you want to validate only the data contained in that mapping instead
        if all of the data in the form post, you might use
        ``form.validate(controls, subcontrol='user')``.
        """
        try:
            pstruct = peppercorn.parse(controls)
        except ValueError as e:
            exc = colander.Invalid(self.schema,
                                   "Invalid peppercorn controls: %s" % e)
            self.widget.handle_error(self, exc)
            cstruct = colander.null
            raise exception.ValidationFailure(self, cstruct, exc)
        if subcontrol is not None:
            pstruct = pstruct.get(subcontrol, colander.null)
        return self.validate_pstruct(pstruct)

    def validate_pstruct(self, pstruct):
        """
        Validate the pstruct passed.  Works exactly like the
        :class:`deform.field.validate` method, except it accepts a pstruct
        instead of a set of form controls.  A usage example follows::

          if 'submit' in request.POST:  # the form submission needs validation
              controls = request.POST.items()
              pstruct = peppercorn.parse(controls)
              substruct = pstruct['submapping']
              try:
                  deserialized = form.validate_pstruct(substruct)
                  do_something(deserialized)
                  return HTTPFound(location='http://example.com/success')
              except ValidationFailure, e:
                  return {'form':e.render()}
          else:
              return {'form':form.render()} # the form just needs rendering
        """

        exc = None

        try:
            cstruct = self.deserialize(pstruct)
        except colander.Invalid as e:
            # fill in errors raised by widgets
            self.widget.handle_error(self, e)
            cstruct = e.value
            exc = e

        self.cstruct = cstruct

        try:
            appstruct = self.schema.deserialize(cstruct)
        except colander.Invalid as e:
            # fill in errors raised by schema nodes
            self.widget.handle_error(self, e)
            exc = e

        if exc:
            raise exception.ValidationFailure(self, cstruct, exc)

        return appstruct

    def _get_cstruct(self):
        return self._cstruct

    def _set_cstruct(self, cstruct):
        self._cstruct = cstruct
        child_cstructs = self.schema.cstruct_children(cstruct)
        if not isinstance(child_cstructs, colander.SequenceItems):
            # If the schema's type returns SequenceItems, it means that the
            # node is a sequence node, which means it has one child
            # representing its prototype instead of a set of "real" children;
            # our widget handle cloning the prototype node.  The prototype's
            # cstruct will already be set up with its default value by virtue
            # of set_appstruct having been called in its constructor, and we
            # needn't (and can't) do anything more.
            for n, child in enumerate(self.children):
                child.cstruct = child_cstructs[n]

    def _del_cstruct(self):
        if "_cstruct" in self.__dict__:
            # rely on class-scope _cstruct (null)
            del self._cstruct

    cstruct = property(_get_cstruct, _set_cstruct, _del_cstruct)

    def __repr__(self):
        return "<%s.%s object at %d (schemanode %r)>" % (
            self.__module__,
            self.__class__.__name__,
            id(self),
            self.schema.name,
        )

    def set_appstruct(self, appstruct):
        """Set the cstruct of this node (and its child nodes) using
        ``appstruct`` as input."""
        cstruct = self.schema.serialize(appstruct)
        self.cstruct = cstruct
        return cstruct

    def set_pstruct(self, pstruct):
        """Set the cstruct of this node (and its child nodes) using
        ``pstruct`` as input."""
        try:
            cstruct = self.deserialize(pstruct)
        except colander.Invalid as e:
            # explicitly don't set errors
            cstruct = e.value
        self.cstruct = cstruct

    def render_template(self, template, **kw):
        """Render the template named ``template`` using ``kw`` as the
        top-level keyword arguments (augmented with ``field`` and ``cstruct``
        if necessary)"""
        values = {"field": self, "cstruct": self.cstruct}
        values.update(kw)  # allow caller to override field and cstruct
        return self.renderer(template, **values)

    # retail API

    def start_mapping(self, name=None):
        """Create a start-mapping tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """
        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__start__" value="%s:mapping"/>'
        return Markup(tag % (name, ))

    def end_mapping(self, name=None):
        """Create an end-mapping tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """
        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__end__" value="%s:mapping"/>'
        return Markup(tag % (name, ))

    def start_sequence(self, name=None):
        """Create a start-sequence tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """
        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__start__" value="%s:sequence"/>'
        return Markup(tag % (name, ))

    def end_sequence(self, name=None):
        """Create an end-sequence tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """

        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__end__" value="%s:sequence"/>'
        return Markup(tag % (name, ))

    def start_rename(self, name=None):
        """Create a start-rename tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """
        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__start__" value="%s:rename"/>'
        return Markup(tag % (name, ))

    def end_rename(self, name=None):
        """Create a start-rename tag (a literal).  If ``name`` is ``None``,
        the name of this node will be used to generate the name in the tag.
        See the :term:`Peppercorn` documentation for more information.
        """
        if name is None:
            name = self.name
        tag = '<input type="hidden" name="__end__" value="%s:rename"/>'
        return Markup(tag % (name, ))
Exemplo n.º 29
0
class MenuSchema(colander.MappingSchema):
    name = colander.SchemaNode(colander.String())
    price = colander.SchemaNode(colander.Decimal(quant='1.00', rounding=decimal.ROUND_UP))  # 2dp, rounded up
    currency = colander.SchemaNode(Currency(), validator=Currency.is_valid, missing='JPY')
    images = Images()
    tags = Tags()
Exemplo n.º 30
0
    def get_schema_from_column(self, prop, overrides):
        """ Build and return a :class:`colander.SchemaNode` for a given Column.

        This method uses information stored in the column within the ``info``
        that was passed to the Column on creation.  This means that
        ``Colander`` options can be specified declaratively in
        ``SQLAlchemy`` models using the ``info`` argument that you can
        pass to :class:`sqlalchemy.Column`.

        Arguments/Keywords

        prop
            A given :class:`sqlalchemy.orm.properties.ColumnProperty`
            instance that represents the column being mapped.
        overrides
            A dict-like structure that consists of schema attributes to
            override imperatively. Values provides as part of :attr:`overrides`
            will take precendence over all others.
        """

        # The name of the SchemaNode is the ColumnProperty key.
        name = prop.key
        kwargs = dict(name=name)
        column = prop.columns[0]
        typedecorator_overrides = getattr(column.type, self.ca_class_key,
                                          {}).copy()
        declarative_overrides = column.info.get(self.sqla_info_key, {}).copy()
        self.declarative_overrides[name] = declarative_overrides.copy()

        key = 'exclude'

        if key not in itertools.chain(declarative_overrides, overrides) \
           and typedecorator_overrides.pop(key, False):
            log.debug('Column %s skipped due to TypeDecorator overrides', name)
            return None

        if key not in overrides and declarative_overrides.pop(key, False):
            log.debug('Column %s skipped due to declarative overrides', name)
            return None

        if overrides.pop(key, False):
            log.debug('Column %s skipped due to imperative overrides', name)
            return None

        self.check_overrides(name, 'name', typedecorator_overrides,
                             declarative_overrides, overrides)

        for key in ['missing', 'default']:
            self.check_overrides(name, key, typedecorator_overrides, {}, {})

        # The SchemaNode built using the ColumnProperty has no children.
        children = []

        # The type of the SchemaNode will be evaluated using the Column type.
        # User can overridden the default type via Column.info or
        # imperatively using overrides arg in SQLAlchemySchemaNode.__init__

        # Support sqlalchemy.types.TypeDecorator
        column_type = getattr(column.type, 'impl', column.type)

        imperative_type = overrides.pop('typ', None)
        declarative_type = declarative_overrides.pop('typ', None)
        typedecorator_type = typedecorator_overrides.pop('typ', None)

        if imperative_type is not None:
            if hasattr(imperative_type, '__call__'):
                type_ = imperative_type()
            else:
                type_ = imperative_type
            log.debug('Column %s: type overridden imperatively: %s.', name,
                      type_)

        elif declarative_type is not None:
            if hasattr(declarative_type, '__call__'):
                type_ = declarative_type()
            else:
                type_ = declarative_type
            log.debug('Column %s: type overridden via declarative: %s.', name,
                      type_)

        elif typedecorator_type is not None:
            if hasattr(typedecorator_type, '__call__'):
                type_ = typedecorator_type()
            else:
                type_ = typedecorator_type
            log.debug('Column %s: type overridden via TypeDecorator: %s.',
                      name, type_)

        elif isinstance(column_type, Boolean):
            type_ = colander.Boolean()

        elif isinstance(column_type, Date):
            type_ = colander.Date()

        elif isinstance(column_type, DateTime):
            type_ = colander.DateTime(default_tzinfo=None)

        elif isinstance(column_type, Enum):
            type_ = colander.String()
            kwargs["validator"] = colander.OneOf(column.type.enums)

        elif isinstance(column_type, Float):
            type_ = colander.Float()

        elif isinstance(column_type, Integer):
            type_ = colander.Integer()

        elif isinstance(column_type, String):
            type_ = colander.String()
            kwargs["validator"] = colander.Length(0, column.type.length)

        elif isinstance(column_type, Numeric):
            type_ = colander.Decimal()

        elif isinstance(column_type, Time):
            type_ = colander.Time()

        else:
            raise NotImplementedError(
                'Not able to derive a colander type from sqlalchemy '
                'type: %s  Please explicitly provide a colander '
                '`typ` for the "%s" Column.' % (repr(column_type), name))
        """
        Add default values

        possible values for default in SQLA:
         1. plain non-callable Python value
              - give to Colander as a default
         2. SQL expression (derived from ColumnElement)
              - leave default blank and allow SQLA to fill
         3. Python callable with 0 or 1 args
            1 arg version takes ExecutionContext
              - leave default blank and allow SQLA to fill

        all values for server_default should be ignored for
        Colander default
        """
        if (isinstance(column.default, ColumnDefault)
                and column.default.is_scalar):
            kwargs["default"] = column.default.arg
        """
        Add missing values

        possible values for default in SQLA:
         1. plain non-callable Python value
              - give to Colander as a missing unless nullable
         2. SQL expression (derived from ColumnElement)
              - set missing to 'drop' to allow SQLA to fill this in
                and make it an unrequired field
         3. Python callable with 0 or 1 args
            1 arg version takes ExecutionContext
              - set missing to 'drop' to allow SQLA to fill this in
                and make it an unrequired field

        if nullable, then missing = colander.null (this has to be
        the case since some colander types won't accept `None` as
        a value, but all accept `colander.null`)

        all values for server_default should result in 'drop'
        for Colander missing

        autoincrement results in drop
        """
        if isinstance(column.default, ColumnDefault):
            if column.default.is_callable:
                kwargs["missing"] = drop
            elif column.default.is_clause_element:  # SQL expression
                kwargs["missing"] = drop
            elif column.default.is_scalar:
                kwargs["missing"] = column.default.arg
        elif column.nullable:
            kwargs["missing"] = colander.null
        elif isinstance(column.server_default, FetchedValue):
            kwargs["missing"] = drop  # value generated by SQLA backend
        elif (hasattr(column.table, "_autoincrement_column")
              and id(column.table._autoincrement_column) == id(column)):
            # this column is the autoincrement column, so we can drop
            # it if it's missing and let the database generate it
            kwargs["missing"] = drop

        kwargs.update(typedecorator_overrides)
        kwargs.update(declarative_overrides)
        kwargs.update(overrides)

        return colander.SchemaNode(type_, *children, **kwargs)