-
Notifications
You must be signed in to change notification settings - Fork 301
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sorting and filtering on hybrid properties fails #562
Comments
So |
Looking into this a bit more, if I print out what arg1 is in https://github.com/jfinkels/flask-restless/blob/master/flask_restless/search/operators.py#L173 it looks like it is the select statement returned by the When printed it looks like this: SELECT flask.utilities_workorder_feature.feature_status
FROM flask.utilities_workorder_feature, flask.utilities_workorder
WHERE flask.utilities_workorder_feature.workorder_id = flask.utilities_workorder.id And the type is |
This is two separate issues: 1. using |
Okay, I think I understand what the issue is, but I'm not sure how to resolve this. Your assumption was that the client will be able to make filtering queries based on the value of the hybrid attribute (the method decorated by In summary, you are trying to filter on instance attributes, but filtering is done at the class level (representing SQL expressions), so the hybrid property expression must be used. I'm not sure if there's a workaround for this, you may have to change your model or change the client. In any case, if we cannot figure out a way to resolve this, there should at least be a better error message. |
I've been looking into solving this issue on my end, but I'm really confused. What do you mean by this?
Do you have any ideas on how my model should be changed? Every example I've seen in which hybrid_properties are used with relationships uses a select(.....) statement. |
See the Hybrid Attributes section of the SQLAlchemy documentation: "In many cases, the construction of an in-Python function and a SQLAlchemy SQL expression have enough differences that two separate Python expressions should be defined." In the example there, there is a "hybrid property", which is a function on an instance of the model, and there is a separate "expression", which is a function on the model class. In Flask-Restless, when you have an
the Flask-Restless search code issues a SQL query like
It is using the "expression" form of the hybrid property, not the instance attribute form. In your example, you expected the query to execute the first However, reading more carefully the Correlated Subquery Relationship Hybrid section of the documentation, I see that you are using the pattern suggested there. So Flask-Restless should be able to interpret the select query somehow, you shouldn't have to change your model. Looking at the example in that section: >>> print s.query(User).filter(User.balance > 400)
SELECT "user".id AS user_id, "user".name AS user_name
FROM "user"
WHERE (SELECT sum(account.balance) AS sum_1
FROM account
WHERE account.user_id = "user".id) > :param_1 makes me think that maybe this is something weird about using the "like" operator with a select statement? I'm not sure. |
Thanks for the tips, seriously I really appreciate you taking the time. I guess I didn't really understand what was going on with the I modified my expression to be this: @is_complete.expression
def is_complete(cls):
return case({True: 'Yes', False: 'No'},
select([func.count(Workorder_Feature.id)]).\
where(Workorder_Feature.workorder_id==cls.id).\
where(Workorder_Feature.feature_status == 'Open') == 0
) And now I'm able to sort and filter. Even with |
Okay, I guess I was a bit preemptively overzealous. While I'm able to get results when sort/filter without errors, it doesn't look like the data is actually getting filtered or sorted on the hybrid property. When sorting, it returns the data in the same order regardless of ascending/descending. Filtering while throwing no errors isn't actually filtering correctly either. filter on "Yes"
produces the json {
"data": [],
"included": [],
"jsonapi": {
"version": "1.0"
},
"links": {
"first": "http://greggtest.cityhall.com:5000/api/workorder?filter[objects]=[{\"name\":\"is_complete\",\"op\":\"equals\",\"val\":\"Yes\"}]&page[number]=1&filter[objects]=[{\"name\": \"is_complete\", \"op\": \"equals\", \"val\": \"Yes\"}]&page[size]=10",
"last": "http://greggtest.cityhall.com:5000/api/workorder?filter[objects]=[{\"name\":\"is_complete\",\"op\":\"equals\",\"val\":\"Yes\"}]&page[number]=0&filter[objects]=[{\"name\": \"is_complete\", \"op\": \"equals\", \"val\": \"Yes\"}]&page[size]=10",
"next": "http://greggtest.cityhall.com:5000/api/workorder?filter[objects]=[{\"name\":\"is_complete\",\"op\":\"equals\",\"val\":\"Yes\"}]&page[number]=2&filter[objects]=[{\"name\": \"is_complete\", \"op\": \"equals\", \"val\": \"Yes\"}]&page[size]=10",
"prev": "http://greggtest.cityhall.com:5000/api/workorder?filter[objects]=[{\"name\":\"is_complete\",\"op\":\"equals\",\"val\":\"Yes\"}]&page[number]=0&filter[objects]=[{\"name\": \"is_complete\", \"op\": \"equals\", \"val\": \"Yes\"}]&page[size]=10",
"self": "/api/workorder"
},
"meta": {
"total": 0
}
} filter on "No" (truencated)
{
"data": [
{
"attributes": {
"created_by": null,
"date_completed": null,
"date_created": "2016-11-03T14:14:00.713000",
"date_modified": "2016-11-03T14:14:00.713000",
"details": "Yo",
"is_complete": "Yes",
"location": "Yo",
"modified_by": null
},
"id": "76",
"links": {
"self": "http://greggtest.cityhall.com:5000/api/workorder/76"
},
"relationships": {
"comments": {
"data": [],
"links": {
"related": "/api/workorder/76/comments",
"self": "/api/workorder/76/relationships/comments"
}
},
"crew": {
"data": {
"id": "2",
"type": "workorder_crew"
},
"links": {
"related": "/api/workorder/76/crew",
"self": "/api/workorder/76/relationships/crew"
}
},
"features": {
"data": [],
"links": {
"related": "/api/workorder/76/features",
"self": "/api/workorder/76/relationships/features"
}
},
"status": {
"data": {
"id": "2",
"type": "workorder_status"
},
"links": {
"related": "/api/workorder/76/status",
"self": "/api/workorder/76/relationships/status"
}
},
"task": {
"data": {
"id": "1",
"type": "workorder_task"
},
"links": {
"related": "/api/workorder/76/task",
"self": "/api/workorder/76/relationships/task"
}
}
},
"type": "workorder"
}, |
Can you post the complete code for a minimal working example? (No extraneous attributes, for example.) |
Yes, sir, here's a complete example. https://gist.github.com/roemhildtg/435fd78be14c2f0459e078a3a14fa476 |
I think you need to double check your hybrid property expression. The part with Regardless, I'd like to add a test for filtering and sorting using a hybrid expression so that when this comes up in the future, I can point to the tests and say for sure that the problem is not in Flask-Restless. |
I've finally gotten to the bottom of this... First I didn't realize that the final hybrid property in case it helps someone: @hybrid_property
def is_complete(self):
"""
when all features are completed, this workorder should be completed
"""
features = Workorder_Feature.query.filter(Workorder_Feature.workorder_id == self.id)
for f in features:
if f.feature_status == 'Open':
return 'No'
return 'Yes'
@is_complete.expression
def is_complete(cls):
count = select([func.count(Workorder_Feature.id)]).\
where(Workorder_Feature.workorder_id==cls.id).\
where(Workorder_Feature.feature_status == 'Open').label('count')
return case({0: 'Yes'}, count, else_='No') |
I have added unit tests for this situation in pull request #621. I'm going to close this as not an issue with Flask-Restless. Hopefully the discussion here helps someone with this issue in the future. Thanks for continuing to report issues @roemhildtg! |
Getting this and no idea how to fix:
class PlayerGroups(db.Model):
members = db.relationship('Players', back_populates='group')
@hybrid_property
def count(self):
return len(self.members)
@count.expression
def count(cls):
return db.select([db.func.count(Players.id)]).where(
Players.group_id == cls.id).label('count') Happens when I try to POST / create a new group. |
I'm using flask_restless-1.0.0b2 and it seems that if I define a hybrid property like
And I pass parameters like this:
or
It throws errors in
flask_restless\search\operators.py
on line 98 https://github.com/jfinkels/flask-restless/blob/master/flask_restless/search/operators.py#L98:'Select' has no attribute 'like'
and in flask_restless\search\drivers.py on line 121 https://github.com/jfinkels/flask-restless/blob/master/flask_restless/search/drivers.py#L121:
Select object has no attribute 'asc'
The text was updated successfully, but these errors were encountered: