FormAlchemy / formalchemy

MIT License
81 stars 29 forks source link

hybrid_properties #20

Closed marplatense closed 10 years ago

marplatense commented 12 years ago

I was under the impression issue 11 would include hybrid_properties but I cannot make them work. Consider the following example where gender is a synonym for _gender:

  from formalchemy import Column
  from formalchemy.forms import FieldSet
  from sqlalchemy import Table, Unicode, Integer, create_engine, MetaData
  from sqlalchemy.ext.declarative import declarative_base
  from sqlalchemy.ext.hybrid import hybrid_property
  from sqlalchemy.orm import sessionmaker
  session = sessionmaker()
  Session = session()
  engine = create_engine('postgresql://usr:pass@localhost/db', echo=True)
  Session.configure(bind=engine)
  m = MetaData()
  m.bind = engine
  base = declarative_base(metadata=m)

  class Test(base):
      __tablename__ = "example"
      id = Column(Integer, autoincrement=True, primary_key=True)
      name = Column(Unicode(100), nullable=False)
      _gender = Column(Unicode(1), nullable=False, default=True)
     @hybrid_property
     def gender(self):
          return self._gender
     @gender.setter
     def gender(self, value):
          if value.upper() not in ('F', 'M'):
              raise(Exception(u'Gender must be F or M'))
          else:
              self._gender = value.upper()

  m.create_all()

  fs1 = FieldSet(Test())
  fs1.configure(include=[fs1.name, fs1.gender])
  fs1.render()

When I execute it, I'm getting:

  Traceback (most recent call last):
    File "<ipython-input-3-37113d7b6e1e>", line 1, in <module>
      import property_fa as f
    File "property_fa.py", line 35, in <module>
      fs1.configure(include=[fs1.name, fs1.gender])
    File "/home/mariano/Code/example/env/lib/python2.7/site-packages/FormAlchemy-1.4.1-py2.7.egg/formalchemy/forms.py", line 773, in __getattr__
      raise AttributeError(attrname)
  AttributeError: gender

If I use _gender as the included field, of course it works however it bypasses my property control.

TIA, Mariano

smurfix commented 10 years ago

formalchemy works by analyzing sqlalchemy's internal data structures. The problem is that hybrid_property is just a utility function which implements a fancy way of generating a property class; it doesn't hook into sqlalchemy at all, so formalchemy doesn't see it.

Therefore, you need to create the 'gender' attribute yourself. This is simple enough:

from formalchemy.fields import AttributeField
t = Test()
fs1 = FieldSet(t)
gf = AttributeField(Test.gender,fs1)
gf.name="gender"
fs1.append(gf)
fs1.configure(include=[fs1.name, fs1.gender])
fs1.render()
mikepk commented 9 years ago

I just hit this same problem, what's odd is that older versions of FormAlchmey did seem to work with synonyms and hybrid_property but newer versions don't. I'm not sure exactly where the break happened but FormAlchemy==1.4.3 does allow them. In the following example 'password' is a synonym for _password. The only difference between the two was moving from my pinned FA version to the latest.

1.4.3

>>> from formalchemy import FieldSet
>>> fs = FieldSet(User)
>>> print fs.password
AttributeField(password)

1.5.5

>>> from formalchemy import FieldSet
>>> fs = FieldSet(User)
>>> print fs.password
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/share/ps/venv/qs2/local/lib/python2.7/site-packages/formalchemy/forms.py", line 799, in __getattr__
    raise AttributeError(attrname)
AttributeError: password
mikepk commented 9 years ago

Created a pull request #64 with changes restoring the older synonym / hybrid_property exposing behavior.