jfinkels/flask-restless — pull request #359 by TimotheeJeannin (2014-10-02 08:24:05+00:00)
Title: Allow POST and PATCH requests to modify hybrid properties.

The current implementations of [patch](https://github.com/jfinkels/flask-restless/blob/master/flask_restless/views.py#L1288) and [post](https://github.com/jfinkels/flask-restless/blob/master/flask_restless/views.py#L1165) are checking for any request parameter naming a column which does not exist on the current model.

```python
for field in data:
if not has_field(self.model, field):
msg = "Model does not have field '{0}'".format(field)
return dict(message=msg), 400
```

The [has_field](https://github.com/jfinkels/flask-restless/blob/master/flask_restless/helpers.py#L148) function returns `True` if the model has the specified field and **it is not a hybrid property**.

```python
def has_field(model, fieldname):
"""Returns ``True`` if the `model` has the specified field, and it is not
a hybrid property.

"""
return (hasattr(model, fieldname) and
not isinstance(getattr(model, fieldname), _BinaryExpression))
```

As a result posting or patching a model with some hybrid attributes will return a *status code 400*.

From the [SqlAlchemy documentation on Hybrid Attributes](http://docs.sqlalchemy.org/en/rel_0_7/orm/extensions/hybrid.html#defining-setters), it is possible to define a setter for an hybrid property.

It would be very nice to be able to post or patch a model with a settable hybrid property.

For example, doing a :

```python
self.app.post('/api/screen', data=dumps({"width": 5, "number_of_pixels": 50}))
```

On a model looking like this:

```python
class Screen(self.Base):
__tablename__ = 'screen'
id = Column(Integer, primary_key=True)
width = Column(Integer, nullable=False)
height = Column(Integer, nullable=False)

@hybrid_property
def number_of_pixels(self):
return self.width * self.height

@number_of_pixels.setter
def number_of_pixels(self, value):
self.height = value / self.width
```

Inside the patch and post implementation, I tried to find a way to detect if a hybrid property is settable or not without success. The [hybrid_property decorator implementation](https://github.com/zzzeek/sqlalchemy/blob/7fc08fe89af9760750899346cf81bd74e0d9150f/lib/sqlalchemy/ext/hybrid.py#L706) is overriding the `__set__` method and I couldn't find a way to detect that.

```python
class hybrid_property(interfaces.InspectionAttr):
"""A decorator which allows definition of a Python descriptor with both
instance-level and class-level behavior.

"""

is_attribute = True
extension_type = HYBRID_PROPERTY

def __init__(self, fget, fset=None, fdel=None, expr=None):
self.fget = fget
self.fset = fset

[...]

def __set__(self, instance, value):
if self.fset is None:
raise AttributeError("can't set attribute")
self.fset(instance, value)

[...]
```

The cleanest way I found to handle this is to try to set the attribute and if `AttributeError("can't set attribute")` is raised, react accordingly.

Let me know if there is things you like to see improved in the pull request.

Fixes #320.