-
Notifications
You must be signed in to change notification settings - Fork 228
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
Add a way to serve custom properties on model classes #103
Comments
I just googled and get here, this feature is exactly what I need, wondering why it is still lacking. |
I'd say this is a wanted feature and a needed feature. It may be a little difficult to implement efficiently though. And I think that is why |
I messed around a bit with this. You can currently do this by making your property a For example: class MyModel(...):
id = Column(Integer)
@hybrid_property
def above_ten(self):
return self.id > 10
class MyModelType(SQLAlchemyObjectType):
class Meta:
model = Meta
def resolve_above_ten(self, _):
return self.above_ten If you use a regular property. You get the "Cannot find query field X" error. |
@Kazanz - right, since version 2.0 hybrid properties are supported. But they are not pure SQLAlchemy (it's an extension) and actually used for a different purpose. The decorator The other problem I mentioned is that hybrid properties currently are only translated to |
Posted the above for people who want to use properties in the interim.
Agreed. You should be able to declare the which properties you want available on the graphql type without changing models else where, otherwise its coupled too tightly.
Another option is to make a
While I personally prefer the type annotation approach, not every uses type annotations and it would be a big leap to force type annotation on end users. So, I lean toward the the approach of an |
That would work, but require adding a resolver for the sole purpose of specifying the type. My idea was actually to introspect the return type of the model class property. But again, that would require changes in the model and we seem to agree that we don't want this - or at least not as the only possible way. |
Yes, I agree. at the minimum there should be a way to do it without updating your models. I have worked on a project with hundreds of models containing many properties and it would be a bear to convert them all over. Also I think that by default no properties are allowed unless explicitly set as some properties could have side effects. What about an option where you explicitly define the type on the Something like:
Similar to adding properties to serializers in DRF. |
Either that, or:
And if |
I think
Wouldn't have to worry about custom types as they could be passed to include_properties, but would be useful if you have properties that return multiple types (like a property that returns a string or null). |
Is there any update on this capability? What can be done in the meantime? |
Just bumping this again to inquire. |
Hi, we're also very interested in this capability :) |
Still Interested! |
Messing around a bit more with what @Kazanz proposed I came up with something that could be used like this: class Foo(graphene_sqlalchemy.SQLAlchemyObjectType):
class Meta:
model = UnrelatedField.enable(MyModel)
foo = UnrelatedField(type=graphene.String)
def resolve_foo(self, info):
return "Hello World!" Using a import sqlalchemy.ext.hybrid
import graphene_sqlalchemy.types
class UnrelatedField(graphene_sqlalchemy.types.ORMField):
"""
ORMField hiding an (ugly) dummy field on the Model.
Usage:
class Foo(graphene_sqlalchemy.SQLAlchemyObjectType):
class Meta:
model = UnrelatedField.enable(MyModel)
foo = UnrelatedField(type=graphene.String)
def resolve_foo(self, info):
return "Hello World!"
"""
__modelattrname = "_gsa_unrelated_catchall"
def __init__(self, **kwargs):
"""Initialize ORMField with kwargs and a catchall model_attr."""
if "model_attr" in kwargs:
raise TypeError("UnrelatedField can not depend on model_attr.")
return super().__init__(model_attr=self.__modelattrname, **kwargs)
@classmethod
def enable(cls, Model):
"""Return a subclass of Model for use with UnrelatedField"""
return type("%sGSAProxy" % Model.__name__, (Model,), {
cls.__modelattrname: sqlalchemy.ext.hybrid.hybrid_property(lambda *_: None)
})
|
Not sure if I understand the question (requirements) correctly, but what I needed was just to add a custom field to an SQLAlchemy type returned by one of my root queries, and (after Googling and reading some documentation without success) finally came up with rather simple solution (meta-ish code):
..if this helps anybody? |
@Raekkeri this is exactly what I'm looking for. But this function does not receive input query arguments. For the solution, I put the arguments or immediately custom fields in the object and return it here class GetMessages(SQLAlchemyObjectType):
class Meta:
model = Message
is_sent = graphene.Field(graphene.Boolean)
def resolve_is_sent(self, info):
return self.is_sent_custom_field # put in another resolver |
Bumping this again. Currently, there's not much of a way to serve any kind of annotated/labeled data either. So you're stuck with fields restricted to a subset of model fields. Having to introduce a hybrid property on a model just for this seems excessive and unnecessary. EDIT : Found a way to perform this through query expressions . See this :
|
Picking this up again since we recently pushed forward the use of type annotations for the generation of sqlalchemy-schema fields (#340). It is still in evaluation, since supporting If we go forward with that, it might make sense to add support for properties as well. Re-using the type conversion implemented for |
In SQLAlchemy, you often want to provide additional custom properties on the model classes, like this:
Unfortunately, such properties are currently not made accessible in GraphQL by graphene_sqlalchemy. While there is a way to exclude existing column properties using the Meta attributes
exclude_fields
andonly_fields
, there is no way to include custom properties such asfull_name
from the above model.One possible solution is to use hybrid properties which are now supported by graphene_sqlalchemy since 2.0. However, "ordinary" properties should also be supported. Often you dont really want or need hybrid properties - it is not always necessary or feasible to provide the property on the class level. Also, the conversion of hybrid properties currently has the limitation that it always generates
String
fields.So I believe we need some mechanism for proxying arbitrary properties from SQLAlchemy to Graphene objects. I'm not yet sure whether all custom public properties (i.e. those not starting with an underscore) except those who are explicitly excluded should be proxied by default (like it is done with hybrid properties), or whether there should be a Meta attribute with a list of properties that must be explicitly set.
We probably also need to add a way to specify which type the (normal or hybrid) properties shall be converted to. This could also be defined in a Meta attribute, or maybe we could check for Python type hints if nothing is defined, like this:
I am willing to contribute with code, but first I want to get some feedback regarding this idea from others.
The text was updated successfully, but these errors were encountered: