clean up formalchemy stuff

This commit is contained in:
Lance Edgar 2012-07-26 14:00:19 -07:00
parent 1934ff00e1
commit 65af2a1855
4 changed files with 332 additions and 218 deletions

View file

@ -32,27 +32,26 @@ import datetime
import new
from pyramid.renderers import render
from webhelpers import paginate
from webhelpers.html import tags
from webhelpers.html.builder import format_attrs
from webhelpers.html.tags import literal
import formalchemy
from formalchemy import fields
from formalchemy.validators import accepts_none
import edbob
from edbob.lib import pretty
from edbob.util import prettify
from edbob.pyramid import Session, helpers
from edbob.pyramid.forms.formalchemy.fieldset import *
from edbob.pyramid.forms.formalchemy.grid import *
from edbob.pyramid.forms.formalchemy.fields import *
from edbob.pyramid.forms.formalchemy.renderers import *
__all__ = ['AlchemyGrid', 'ChildGridField', 'PropertyField',
'EnumFieldRenderer', 'PrettyDateTimeFieldRenderer',
'AutocompleteFieldRenderer', 'FieldSet',
'make_fieldset', 'required', 'pretty_datetime']
'make_fieldset', 'required', 'pretty_datetime',
'AssociationProxyField']
class TemplateEngine(formalchemy.templates.TemplateEngine):
@ -70,193 +69,6 @@ engine = TemplateEngine()
formalchemy.config.engine = engine
class AlchemyGrid(formalchemy.Grid):
"""
This class defines the basic grid which you see in pretty much all
Rattail/Pyramid apps.
.. todo::
This needs to be documented more fully, along with the rest of
rattail.pyramid I suppose...
"""
prettify = staticmethod(prettify)
# uuid_key = None
# def __init__(self, cls, instances, config, url_kwargs={}, *args, **kwargs):
# formalchemy.Grid.__init__(self, cls, instances, *args, **kwargs)
# self.pager = instances if isinstance(instances, paginate.Page) else None
# self.config = config
# self.url_kwargs = url_kwargs
# self.sortable = config.get('sortable', False)
def __init__(self, cls, instances, config, gridurl=None, objurl=None,
delurl=None, **kwargs):
"""
Grid constructor.
``url`` must be the URL used to access the grid itself. This url/view
must accept a GET query string parameter of "partial=True", which will
indicate that the grid *only* is being requested, as opposed to the
full page in which the grid normally resides.
"""
formalchemy.Grid.__init__(self, cls, instances, **kwargs)
self.config = config
self.request = config['request']
self.gridurl = gridurl
self.objurl = objurl
self.delurl = delurl
self.sortable = config.get('sortable', False)
self.deletable = config.get('deletable', False)
self.pager = instances if isinstance(instances, paginate.Page) else None
def field_name(self, field):
return field.name
def iter_fields(self):
for field in self.render_fields.itervalues():
yield field
def render_field(self, field, readonly):
if readonly:
return field.render_readonly()
return field.render()
def row_attrs(self, i):
attrs = dict(class_='even' if i % 2 else 'odd')
if hasattr(self.model, 'uuid'):
attrs['uuid'] = self.model.uuid
return format_attrs(**attrs)
def url_attrs(self):
attrs = {}
url = self.request.route_url
if self.gridurl:
attrs['url'] = self.gridurl
if self.objurl:
attrs['objurl'] = url(self.objurl, uuid='{uuid}')
if self.delurl:
attrs['delurl'] = url(self.delurl, uuid='{uuid}')
return format_attrs(**attrs)
# def render(self, class_=None, **kwargs):
# """
# Renders the grid into HTML, and returns the result.
# ``class_`` (if provided) is used to define the class of the ``<div>``
# (wrapper) and ``<table>`` elements of the grid.
# Any remaining ``kwargs`` are passed directly to the underlying
# ``formalchemy.Grid.render()`` method.
# """
# kwargs['class_'] = class_
# # kwargs.setdefault('get_uuid', self.get_uuid)
# kwargs.setdefault('checkboxes', False)
# return formalchemy.Grid.render(self, **kwargs)
def render(self, **kwargs):
engine = self.engine or formalchemy.config.engine
if self.readonly:
return engine('grid_readonly', grid=self, **kwargs)
kwargs.setdefault('request', self._request)
return engine('grid', grid=self, **kwargs)
def th_sortable(self, field):
class_ = ''
label = field.label()
if self.sortable and field.key in self.config.get('sort_map', {}):
class_ = 'sortable'
if field.key == self.config['sort']:
class_ += ' sorted ' + self.config['dir']
label = literal('<a href="#">') + label + literal('</a>')
if class_:
class_ = ' class="%s"' % class_
return literal('<th' + class_ + ' field="' + field.key + '">') + label + literal('</th>')
# def url(self):
# # TODO: Probably clean this up somehow...
# if self.pager is not None:
# u = self.pager._url_generator(self.pager.page, partial=True)
# else:
# u = self._url or ''
# qs = self.query_string()
# if qs:
# if '?' not in u:
# u += '?'
# u += qs
# elif '?' not in u:
# u += '?partial=True'
# return u
# def query_string(self):
# # TODO: Probably clean this up somehow...
# qs = ''
# if self.url_kwargs:
# for k, v in self.url_kwargs.items():
# qs += '&%s=%s' % (urllib.quote_plus(k), urllib.quote_plus(v))
# return qs
def get_actions(self):
"""
Returns an HTML snippet containing ``<td>`` elements for each "action"
defined in the grid.
"""
def get_class(text):
return text.lower().replace(' ', '-')
res = ''
for action in self.config['actions']:
if isinstance(action, basestring):
text = action
cls = get_class(text)
else:
text = action[0]
if len(action) == 2:
cls = action[1]
else:
cls = get_class(text)
res += literal(
'<td class="action %s"><a href="#">%s</a></td>' %
(cls, text))
return res
# def get_uuid(self):
# """
# .. highlight:: none
# Returns a unique identifier for a given record, in the form of an HTML
# attribute for direct inclusion in a ``<tr>`` element within a template.
# An example of what this function might return would be the string::
# 'uuid="420"'
# Rattail itself will tend to use *universally-unique* IDs (true UUIDs),
# but this method may be overridden to support legacy databases with
# auto-increment IDs, etc. Really the only important thing is that the
# value returned be unique across the relevant data set.
# If the concept is unsupported, the method should return an empty
# string.
# """
# def uuid():
# if self.uuid_key and hasattr(self.model, self.uuid_key):
# return getattr(self.model, self.uuid_key)
# if hasattr(self.model, 'uuid'):
# return getattr(self.model, 'uuid')
# if hasattr(self.model, 'id'):
# return getattr(self.model, 'id')
# uuid = uuid()
# if uuid:
# return literal('uuid="%s"' % uuid)
# return ''
class ChildGridField(formalchemy.Field):
"""
Convenience class for including a child grid within a fieldset as a
@ -292,30 +104,6 @@ def required(value, field=None):
raise formalchemy.ValidationError(msg)
def EnumFieldRenderer(enum):
"""
Adds support for enumeration fields.
"""
class Renderer(formalchemy.fields.SelectFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
if value in enum:
return enum[value]
return value
def render(self, **kwargs):
opts = []
for value in sorted(enum):
opts.append((enum[value], value))
return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs)
return Renderer
def pretty_datetime(value):
"""
Formats a ``datetime.datetime`` instance and returns a "pretty"
@ -389,7 +177,7 @@ def AutocompleteFieldRenderer(service_url, display, width='300px', callback=None
return Renderer
class _AutocompleteFieldRenderer(fields.FieldRenderer):
class _AutocompleteFieldRenderer(formalchemy.fields.FieldRenderer):
"""
Implementation for :class:`AutocompleteFieldRenderer` class.
"""

View file

@ -0,0 +1,49 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
################################################################################
#
# edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar
#
# This file is part of edbob.
#
# edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
``edbob.pyramid.forms.formalchemy.fields`` -- Field Classes
"""
import formalchemy
__all__ = ['AssociationProxyField']
def AssociationProxyField(name, **kwargs):
"""
Returns a :class:`Field` class which is aware of SQLAlchemy association
proxies.
"""
class ProxyField(formalchemy.Field):
def sync(self):
if not self.is_readonly():
setattr(self.parent.model, self.name,
self.renderer.deserialize())
kwargs.setdefault('value', lambda x: getattr(x, name))
return ProxyField(name, **kwargs)

View file

@ -0,0 +1,221 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
################################################################################
#
# edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar
#
# This file is part of edbob.
#
# edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
``edbob.pyramid.forms.formalchemy.grid`` -- FormAlchemy Grid
"""
from webhelpers import paginate
from webhelpers.html.builder import format_attrs
from webhelpers.html.tags import literal
import formalchemy
from edbob.util import prettify
__all__ = ['AlchemyGrid']
class AlchemyGrid(formalchemy.Grid):
"""
Provides an "enhanced" version of the :class:`formalchemy.Grid` class.
"""
prettify = staticmethod(prettify)
# uuid_key = None
# def __init__(self, cls, instances, config, url_kwargs={}, *args, **kwargs):
# formalchemy.Grid.__init__(self, cls, instances, *args, **kwargs)
# self.pager = instances if isinstance(instances, paginate.Page) else None
# self.config = config
# self.url_kwargs = url_kwargs
# self.sortable = config.get('sortable', False)
def __init__(self, cls, instances, config, gridurl=None, objurl=None,
delurl=None, **kwargs):
"""
Grid constructor.
``url`` must be the URL used to access the grid itself. This url/view
must accept a GET query string parameter of "partial=True", which will
indicate that the grid *only* is being requested, as opposed to the
full page in which the grid normally resides.
"""
formalchemy.Grid.__init__(self, cls, instances, **kwargs)
self.config = config
self.request = config['request']
self.gridurl = gridurl
self.objurl = objurl
self.delurl = delurl
self.sortable = config.get('sortable', False)
self.clickable = config.get('clickable', False)
self.deletable = config.get('deletable', False)
self.pager = instances if isinstance(instances, paginate.Page) else None
def field_name(self, field):
return field.name
def iter_fields(self):
for field in self.render_fields.itervalues():
yield field
def render_field(self, field, readonly):
if readonly:
return field.render_readonly()
return field.render()
def row_attrs(self, i):
attrs = dict(class_='even' if i % 2 else 'odd')
if hasattr(self.model, 'uuid'):
attrs['uuid'] = self.model.uuid
return format_attrs(**attrs)
def url_attrs(self):
attrs = {}
url = self.request.route_url
if self.gridurl:
attrs['url'] = self.gridurl
if self.objurl:
attrs['objurl'] = url(self.objurl, uuid='{uuid}')
if self.delurl:
attrs['delurl'] = url(self.delurl, uuid='{uuid}')
return format_attrs(**attrs)
# def render(self, class_=None, **kwargs):
# """
# Renders the grid into HTML, and returns the result.
# ``class_`` (if provided) is used to define the class of the ``<div>``
# (wrapper) and ``<table>`` elements of the grid.
# Any remaining ``kwargs`` are passed directly to the underlying
# ``formalchemy.Grid.render()`` method.
# """
# kwargs['class_'] = class_
# # kwargs.setdefault('get_uuid', self.get_uuid)
# kwargs.setdefault('checkboxes', False)
# return formalchemy.Grid.render(self, **kwargs)
def render(self, **kwargs):
engine = self.engine or formalchemy.config.engine
if self.readonly:
return engine('grid_readonly', grid=self, **kwargs)
kwargs.setdefault('request', self._request)
return engine('grid', grid=self, **kwargs)
def th_sortable(self, field):
class_ = ''
label = field.label()
if self.sortable and field.key in self.config.get('sort_map', {}):
class_ = 'sortable'
if field.key == self.config['sort']:
class_ += ' sorted ' + self.config['dir']
label = literal('<a href="#">') + label + literal('</a>')
if class_:
class_ = ' class="%s"' % class_
return literal('<th' + class_ + ' field="' + field.key + '">') + label + literal('</th>')
# def url(self):
# # TODO: Probably clean this up somehow...
# if self.pager is not None:
# u = self.pager._url_generator(self.pager.page, partial=True)
# else:
# u = self._url or ''
# qs = self.query_string()
# if qs:
# if '?' not in u:
# u += '?'
# u += qs
# elif '?' not in u:
# u += '?partial=True'
# return u
# def query_string(self):
# # TODO: Probably clean this up somehow...
# qs = ''
# if self.url_kwargs:
# for k, v in self.url_kwargs.items():
# qs += '&%s=%s' % (urllib.quote_plus(k), urllib.quote_plus(v))
# return qs
def get_actions(self):
"""
Returns an HTML snippet containing ``<td>`` elements for each "action"
defined in the grid.
"""
def get_class(text):
return text.lower().replace(' ', '-')
res = ''
for action in self.config['actions']:
if isinstance(action, basestring):
text = action
cls = get_class(text)
else:
text = action[0]
if len(action) == 2:
cls = action[1]
else:
cls = get_class(text)
res += literal(
'<td class="action %s"><a href="#">%s</a></td>' %
(cls, text))
return res
# def get_uuid(self):
# """
# .. highlight:: none
# Returns a unique identifier for a given record, in the form of an HTML
# attribute for direct inclusion in a ``<tr>`` element within a template.
# An example of what this function might return would be the string::
# 'uuid="420"'
# Rattail itself will tend to use *universally-unique* IDs (true UUIDs),
# but this method may be overridden to support legacy databases with
# auto-increment IDs, etc. Really the only important thing is that the
# value returned be unique across the relevant data set.
# If the concept is unsupported, the method should return an empty
# string.
# """
# def uuid():
# if self.uuid_key and hasattr(self.model, self.uuid_key):
# return getattr(self.model, self.uuid_key)
# if hasattr(self.model, 'uuid'):
# return getattr(self.model, 'uuid')
# if hasattr(self.model, 'id'):
# return getattr(self.model, 'id')
# uuid = uuid()
# if uuid:
# return literal('uuid="%s"' % uuid)
# return ''

View file

@ -0,0 +1,56 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
################################################################################
#
# edbob -- Pythonic Software Framework
# Copyright © 2010-2012 Lance Edgar
#
# This file is part of edbob.
#
# edbob is free software: you can redistribute it and/or modify it under the
# terms of the GNU Affero General Public License as published by the Free
# Software Foundation, either version 3 of the License, or (at your option)
# any later version.
#
# edbob is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for
# more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with edbob. If not, see <http://www.gnu.org/licenses/>.
#
################################################################################
"""
``edbob.pyramid.forms.formalchemy.renderers`` -- Field Renderers
"""
import formalchemy
__all__ = ['EnumFieldRenderer']
def EnumFieldRenderer(enum):
"""
Adds support for enumeration fields.
"""
class Renderer(formalchemy.fields.SelectFieldRenderer):
def render_readonly(self, **kwargs):
value = self.raw_value
if value is None:
return ''
if value in enum:
return enum[value]
return value
def render(self, **kwargs):
opts = []
for value in sorted(enum):
opts.append((enum[value], value))
return formalchemy.fields.SelectFieldRenderer.render(self, opts, **kwargs)
return Renderer