Skip to content

Commit

Permalink
Fix and test handling of missing terms in collections.
Browse files Browse the repository at this point in the history
  • Loading branch information
Michael Howitz committed May 24, 2016
1 parent 797cd3e commit 3829fea
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGES.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ CHANGES
3.3.1 (unreleased)
------------------

- Nothing changed yet.
- Fix handling of missing terms in collections. (See version 2.9 describing
this feature.)


3.3.0 (2016-03-09)
Expand Down
33 changes: 31 additions & 2 deletions src/z3c/form/term.py
Original file line number Diff line number Diff line change
Expand Up @@ -266,7 +266,35 @@ def __init__(self, context, request, form, field, vocabulary, widget):
self.terms = vocabulary


class MissingCollectionTermsVocabulary(MissingTermsMixin,
class MissingCollectionTermsMixin(MissingTermsMixin):
"""`MissingTermsMixin` adapted to collections."""

def getTerm(self, value):
try:
return super(MissingCollectionTermsMixin, self).getTerm(value)
except LookupError:
if self._canQueryCurrentValue():
if value in self._queryCurrentValue():
return self._makeMissingTerm(value)
raise

def getTermByToken(self, token):
try:
return super(
MissingCollectionTermsMixin, self).getTermByToken(token)
except LookupError:
if self._canQueryCurrentValue():
for value in self._queryCurrentValue():
term = self._makeMissingTerm(value)
if term.token == token:
# check if the given token matches the value, if not
# fall back on LookupError, otherwise we might accept
# any crap coming from the request
return term
raise LookupError(token)


class MissingCollectionTermsVocabulary(MissingCollectionTermsMixin,
CollectionTermsVocabulary):
"""ITerms adapter for zope.schema.ICollection based implementations using
vocabulary with missing terms support."""
Expand All @@ -286,6 +314,7 @@ class CollectionTermsSource(SourceTerms):
interfaces.IWidget)


class MissingCollectionTermsSource(MissingTermsMixin, CollectionTermsSource):
class MissingCollectionTermsSource(MissingCollectionTermsMixin,
CollectionTermsSource):
"""ITerms adapter for zope.schema.ICollection based implementations using
source with missing terms support."""
89 changes: 89 additions & 0 deletions src/z3c/form/term.txt
Original file line number Diff line number Diff line change
Expand Up @@ -529,3 +529,92 @@ Finally, there are terms adapters for all collections:
... rating_context, request, None, contextualSourceRatingsField, widget)
>>> [entry.title for entry in terms]
[u'ugly', u'nice', u'great']


Missing terms in collections
############################

Sometimes it happens that a value goes away from the source, but our
stored collection still has this value.

>>> zope.component.provideAdapter(term.MissingCollectionTermsSource)

>>> terms = term.CollectionTerms(
... RatingContext(), request, None, contextualSourceRatingsField, widget)
>>> terms
<z3c.form.term.MissingCollectionTermsSource object at 0x...>
>>> terms.getTermByToken('42')
Traceback (most recent call last):
...
LookupError: 42

The same goes with looking up a term by value:

>>> terms.getTerm(42)
Traceback (most recent call last):
...
LookupError: 42

Ooops, well this works only if the context has the right value for us.
This is because we don't want to accept any crap that's coming from HTML.

>>> class IRatings(zope.interface.Interface):
... ratings = zope.schema.List(
... title=u'Contextual Sourced Ratings',
... value_type=contextualSourceRatingField)
>>> @zope.interface.implementer(IRatings)
... class Ratings(object):
... ratings = None
... base_value = 10

>>> ctx = Ratings()
>>> ctx.ratings = [42, 10]

>>> ratingsWidget = z3c.form.widget.Widget(request)
>>> ratingsWidget.context = ctx
>>> from z3c.form import interfaces
>>> zope.interface.alsoProvides(ratingsWidget, interfaces.IContextAware)
>>> from z3c.form.datamanager import AttributeField
>>> zope.component.provideAdapter(AttributeField)

>>> terms = term.CollectionTerms(
... ctx, request, None, IRatings['ratings'], ratingsWidget)

Here we go:

>>> term = terms.getTerm(42)
>>> missingTerm = terms.getTermByToken('42')

We get the term, we passed the token, the value is coming from the context.

>>> missingTerm.token
'42'
>>> missingTerm.value
42

We cannot figure the title, so we construct one.
Override ``makeMissingTerm`` if you want your own.

>>> missingTerm.title
u'Missing: ${value}'

Still we raise LookupError if the token does not fit the context's value:

>>> missingTerm = terms.getTermByToken('99')
Traceback (most recent call last):
...
LookupError: 99

The same goes with looking up a term by value.
We get the term if the context's value fits:

>>> missingTerm = terms.getTerm(42)
>>> missingTerm.token
'42'

And an exception if it does not:

>>> missingTerm = terms.getTerm(99)
Traceback (most recent call last):
...
LookupError: 99

0 comments on commit 3829fea

Please sign in to comment.