- attached code.txt
Check for invalid property names on mapped objects
We developed this function when porting from an existing SQLObject project. Since SQLObject and SQLAlchemy use different property names, we developed a custom setattr function to throw an error on any attempt to set an unknown property name. This has proven to be quite valuable for us; would it be useful to include this behavior as an option in the core SQLAlchemy?
The code is below. As written, we used it in a common base class for all our objects but in the spirit of SQLAlchemy it could be patched into each mapped class instead.
(If you like the concept but think additional work is needed, you can contact me at barry at exchangeframe dot com.)
@classmethod
def _find_class_descriptor(class_, name):
"""Searches base classes for a descriptor by the specified name.
Returns it if found, returns None otherwise.
"""
# Search ourselves first
if name in class_.__dict__:
class_property = class_.__dict__[name](name)
if hasattr(class_property, '__set__'):
return class_property
# Search base classes recursively. Note we don't follow the same order
# as new-style classes. This will probably be okay.
for base in class_.__bases__:
if hasattr(base, '_find_class_descriptor'):
parent_result = base._find_class_descriptor(name)
if parent_result != None:
return parent_result
return None
def __setattr__(self, name, value):
"""Only allows setting an attribute if it's already there or if it's one
of the special SQLAlchemy attributes. This helps catch errors such
as missing relations or backrefs.
"""
# This handles the case of properties defined using Python's built-in
# "property" feature, where setting the attribute goes through a
# function. We handle properties defined on our class or any of
# our parents.
class_property = self._find_class_descriptor(name)
if class_property:
class_property.__set__(self, value)
return
# Allow setting pre-existing attributes, the special SQLAlchemy
# properties.
exists = hasattr(self, name)
if exists or name.find('_sa_') != -1 or name.startswith('_AssociationProxy_') \
or name in ('_entity_name', '_instance_key'):
self.__dict__[name](name) = value
else:
raise AttributeError, 'Invalid attribute reference on %s: %s' % (self.__class__.__name__, name)
Comments (4)
-
Account Deleted -
repo owner you might want to see if the Elixir guys are interested in this. ActiveMapper is effectively replaced by Elixir.
one thing to keep in mind with this approach is that many classes do need to store attributes which are not mapped.
-
Account Deleted Agreed, sometimes people need to store transient attributes. In the cases where we needed to do that, I used custom descriptors on my class so this function would not throw an error. I felt this was a small price to pay in exchange for the error checking we gained, especially since we were porting a large existing code base. Disclosure: I am a reformed C++ programmer. :-)
Thanks, I will check with the Elixir guys.
-
repo owner - changed status to wontfix
out of scope for SA, elixir might handle
- Log in to comment
Error checker function - I see that the Wiki formatting messed up the in-line code