def convert_lazy_field_to_dynamic(field, registry=None): model = field.document_type def lazy_resolver(root, *args, **kwargs): document = getattr(root, field.name or field.db_name) if document: _type = registry.get_type_for_model(document.document_type) only_fields = _type._meta.only_fields.split(",") if isinstance( _type._meta.only_fields, str) else list() return document.document_type.objects().no_dereference().only( *(list( set((only_fields + [ to_snake_case(i) for i in get_query_fields(args[0]).keys() ]))))).get(pk=document.pk) return None def dynamic_type(): _type = registry.get_type_for_model(model) if not _type: return None return graphene.Field( _type, resolver=lazy_resolver, description=get_field_description(field, registry), ) return graphene.Dynamic(dynamic_type)
def convert_field_to_dynamic(field, registry=None): model = field.document_type def dynamic_type(): _type = registry.get_type_for_model(model) if not _type: return None return graphene.Field(_type, description=get_field_description(field, registry)) return graphene.Dynamic(dynamic_type)
def model_type(self, type, **kwargs): """ Returns a DjangoListObjectType if converting for lists, Field otherwise. """ if in_kwargs_and_true(kwargs, "list"): return self.type_classes[type.model].get_list_type() if in_kwargs_and_true(kwargs, "input") or in_kwargs_and_true( kwargs, "input_field"): raise ValueError("Cannot convert ModelType as input parameter.") return graphene.Dynamic( lambda: graphene.Field(self.type_classes[type.model]))
def create_dynamic_type(field, type_name, registry, required): # Use the Input type node from registry in a dynamic type, and create a union with that # and the ID def dynamic_type(): _type = registry.get_converted_field(type_name) if not _type: raise GraphQLError(f"The type {type_name} does not exist.") return _type( description=getattr(field, "help_text", ""), required=is_required(field, required, True), ) return graphene.Dynamic(dynamic_type)
class CountryType(DjangoObjectType): class Meta: model = Country last_summary = graphene.Field(SummaryType) last_contextual_update = graphene.Field(ContextualUpdateType) contacts = DjangoPaginatedListObjectField( ContactListType, pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='contacts') operating_contacts = DjangoPaginatedListObjectField( ContactListType, pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='operating_contacts') contextual_updates = DjangoPaginatedListObjectField( ContextualUpdateListType, pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='contextual_updates') summaries = DjangoPaginatedListObjectField( SummaryListType, pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='summaries') crises = graphene.Dynamic(lambda: DjangoPaginatedListObjectField( get_type('apps.crisis.schema.CrisisListType'), pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='crises')) entries = graphene.Dynamic(lambda: DjangoPaginatedListObjectField( get_type('apps.entry.schema.EntryListType'), pagination=PageGraphqlPagination(page_size_query_param='pageSize'), accessor='entries')) @staticmethod def get_queryset(queryset, info): # graphene_django/fields.py:57 demands we implement this method # so that we can filter based on request, but we do not need return queryset
def queryset_type(self, type, **kwargs): """ Returns a field for listing. If the data shall be fetched from a queryset returned by its property, the property name is passed via kwargs. """ if in_kwargs_and_true(kwargs, "input") or in_kwargs_and_true( kwargs, "input_field"): raise ValueError("Cannot convert QuerySet as input parameter.") property_name = kwargs.get("property_name", None) return graphene.Dynamic(lambda: DjangoNestableListObjectPermissionsField( type.type.convert( self, list=True ), # listing type is derived from the type passed as the of_type argument property_name=property_name))
def convert_lazy_field_to_dynamic(field, registry=None): model = field.document_type def lazy_resolver(root, *args, **kwargs): if getattr(root, field.name or field.db_name): return getattr(root, field.name or field.db_name).fetch() def dynamic_type(): _type = registry.get_type_for_model(model) if not _type: return None return graphene.Field(_type, resolver=lazy_resolver, description=get_field_description( field, registry)) return graphene.Dynamic(dynamic_type)
def convert_field_to_dynamic(field, registry=None): model = field.document_type def reference_resolver(root, *args, **kwargs): document = getattr(root, field.name or field.db_name) if document: _type = registry.get_type_for_model(field.document_type) only_fields = _type._meta.only_fields.split(",") if isinstance( _type._meta.only_fields, str) else list() return field.document_type.objects().no_dereference().only(*((list( set(only_fields + [ to_snake_case(i) for i in get_query_fields(args[0]).keys() ]))))).get(pk=document.id) return None def cached_reference_resolver(root, *args, **kwargs): if field: _type = registry.get_type_for_model(field.document_type) only_fields = _type._meta.only_fields.split(",") if isinstance( _type._meta.only_fields, str) else list() return field.document_type.objects().no_dereference().only(*(list( set(only_fields + [ to_snake_case(i) for i in get_query_fields(args[0]).keys() ])))).get(pk=getattr(root, field.name or field.db_name)) return None def dynamic_type(): _type = registry.get_type_for_model(model) if not _type: return None elif isinstance(field, mongoengine.ReferenceField): return graphene.Field(_type, resolver=reference_resolver, description=get_field_description( field, registry)) elif isinstance(field, mongoengine.CachedReferenceField): return graphene.Field(_type, resolver=cached_reference_resolver, description=get_field_description( field, registry)) return graphene.Field(_type, description=get_field_description( field, registry)) return graphene.Dynamic(dynamic_type)
def convert_lazy_field_to_dynamic(field, registry=None): model = field.document_type def lazy_resolver(root, *args, **kwargs): document = getattr(root, field.name or field.db_name) if document: queried_fields = list() _type = registry.get_type_for_model(document.document_type) filter_args = list() if _type._meta.filter_fields: for key, values in _type._meta.filter_fields.items(): for each in values: filter_args.append(key + "__" + each) for each in get_query_fields(args[0]).keys(): item = to_snake_case(each) if item in document.document_type._fields_ordered + tuple( filter_args): queried_fields.append(item) return document.document_type.objects().no_dereference().only( *(set((list(_type._meta.required_fields) + queried_fields)))).get(pk=document.pk) return None def dynamic_type(): _type = registry.get_type_for_model(model) if not _type: return None field_resolver = None required = False if field.db_field is not None: required = field.required resolver_function = getattr( registry.get_type_for_model(field.owner_document), "resolve_" + field.db_field, None) if resolver_function and callable(resolver_function): field_resolver = resolver_function return graphene.Field( _type, resolver=field_resolver if field_resolver else lazy_resolver, description=get_field_description(field, registry), required=required, ) return graphene.Dynamic(dynamic_type)
def _get_inverse_connections_for_references(inverse_references: dict) -> list: inverse_connections = [] for cat_name, collections in inverse_references.items(): for col_name, relation_names in collections.items(): for relation_name in relation_names: schema_collection_name = model.get_table_name( cat_name, col_name) inverse_connections.append({ "src_catalog": cat_name, "src_collection": col_name, "src_relation_name": relation_name, "field_name": f"inv_{relation_name}_{cat_name}_{col_name}", "connection_field": graphene.Dynamic( get_inverse_connection_field(schema_collection_name)), }) return inverse_connections
def convert_many_rel_to_djangomodel(field, registry=None, input_flag=None, nested_field=False): """ An override of the original convert function. Takes into account improved fields with ordering and pagination. """ model = field.related_model def dynamic_type(): if input_flag and not nested_field: return DjangoListField(graphene.ID) else: _type = registry.get_type_for_model(model, for_input=input_flag) # get list type of the object if it has one if hasattr(_type, "get_list_type"): _type = _type.get_list_type() if not _type: return elif input_flag and nested_field: return DjangoListField(_type) elif _type._meta.filter_fields or _type._meta.filterset_class: # return nested relations as a field with pagination return DjangoNestableListObjectPermissionsField( _type, required=is_required(field) and input_flag == "create", filterset_class=_type._meta.filterset_class, ) else: return DjangoListField(_type, required=is_required(field) and input_flag == "create") return graphene.Dynamic(dynamic_type)
def get_graphene_query(): base_models = {} # SQLAlchemy model per collection # Sort references so that if a refers to b, a will be handled before b sorted_refs = _get_sorted_references(model) missing_relations = get_fieldnames_for_missing_relations(model) root_connection_fields = {} for ref in sorted_refs: # A reference is a "catalogue:collection" string pattern = re.compile('(\w+):(\w+)') catalog_name, collection_name = re.findall(pattern, ref)[0] collection = model.get_collection(catalog_name, collection_name) json_attributes = get_collection_json_attributes(collection) # Get all references for the collection ref_items = get_collection_references(collection) connections = [ ] # field name and corresponding FilterConnectionField() for key in ref_items.keys(): cat_name, col_name = re.findall(pattern, ref_items[key]["ref"])[0] connections.append({ "dst_name": model.get_table_name(cat_name, col_name), "connection_field": graphene.Dynamic( get_connection_field( model.get_table_name(cat_name, col_name))), "field_name": key }) inverse_references = get_inverse_references(catalog_name, collection_name) inverse_connections = _get_inverse_connections_for_references( inverse_references) missing_rels = missing_relations.get(catalog_name, {}).get(collection_name, []) model_name = model.get_table_name(catalog_name, collection_name) base_model = models[model_name] # SQLAlchemy model object_type_fields = { "__repr__": lambda self: f"SQLAlchemyObjectType {model_name}", **{ connection["field_name"]: connection["connection_field"] for connection in connections }, **{ connection["field_name"]: connection["connection_field"] for connection in inverse_connections }, **get_json_resolvers(catalog_name, collection_name, json_attributes), **get_relation_resolvers(connections), **get_inverse_relation_resolvers(inverse_connections), **get_missing_relation_resolvers(missing_rels), **{rel: graphene.JSONString for rel in missing_rels}, } meta = type( "Meta", (), { "model": base_model, "exclude_fields": exclude_fields, "interfaces": (graphene.relay.Node, ) }) root_connection_class = _create_connection_class( f"{model_name}Root", object_type_fields, meta) rel_connection_class = _create_connection_class( f"{model_name}Rel", { FIELD.SOURCE_VALUE: graphene.String( description=API_FIELD_DESCRIPTION[FIELD.SOURCE_VALUE]), FIELD.SOURCE_INFO: GenericScalar( description=API_FIELD_DESCRIPTION[FIELD.SOURCE_INFO]), API_FIELD.START_VALIDITY_RELATION: DateTime(description=API_FIELD_DESCRIPTION[ API_FIELD.START_VALIDITY_RELATION]), API_FIELD.END_VALIDITY_RELATION: DateTime(description=API_FIELD_DESCRIPTION[ API_FIELD.END_VALIDITY_RELATION]), **object_type_fields, }, meta) # 'type' is not allowed as an attribute name, so skip it as a filterable attribute collection["attributes"].pop('type', None) # Let the FilterConnectionField be filterable on all attributes of the collection attributes = { attr: graphene_type(value["type"], value["description"]) for attr, value in collection["attributes"].items() if not graphene_type(value["type"]) is None } root_connection_fields[f'{model_name}'] = FilterConnectionField( root_connection_class, **attributes) connection_fields[f'{model_name}'] = FilterConnectionField( rel_connection_class, **attributes) # Use root_connection_class for inverse relations as well. No need for bronwaardes here. inverse_connection_fields[f'{model_name}'] = FilterConnectionField( root_connection_class, **attributes) base_models[f'{model_name}'] = base_model Query = type( "Query", (graphene.ObjectType, ), # <collection> = FilterConnectionField(<collection>Connection, filters...) root_connection_fields) return Query
def convert_model_to_input_type(model, input_flag="create", registry=None, exclude=None, only=None): djangoType = registry.get_type_for_model(model) for_queryset = "order_by" in input_flag or "where" in input_flag if "create" in input_flag or "update" in input_flag: model_fields = get_model_fields( model, only_fields=djangoType._meta.input_only_fields, exclude_fields=djangoType._meta.input_exclude_fields, ) else: model_fields = get_model_fields( model, only_fields=djangoType._meta.only_fields, exclude_fields=djangoType._meta.exclude_fields, for_queryset=for_queryset, ) if "where" in input_flag: if (djangoType._meta.where_only_fields != "__all__" or len(djangoType._meta.where_exclude_fields) > 0): assert not ( djangoType._meta.where_only_fields != "__all__" and len(djangoType._meta.where_exclude_fields) > 0 ), "Only one of where_only_fields or where_exclude_fields parameter can be declared" if djangoType._meta.where_only_fields != "__all__": exclude_fields = [ name for name, field in model_fields if name not in djangoType._meta.where_only_fields ] else: exclude_fields = [ name for name, field in model_fields if name in djangoType._meta.where_exclude_fields ] assert (not "id" in exclude_fields ), "the id field is required for whereInputType" model_fields = [(name, field) for name, field in model_fields if name not in exclude_fields] elif "order_by" in input_flag: if (djangoType._meta.order_by_only_fields != "__all__" or len(djangoType._meta.order_by_exclude_fields) > 0): assert not ( djangoType._meta.order_by_only_fields != "__all__" and len(djangoType._meta.order_by_exclude_fields) > 0 ), "Only one of order_by_only_fields or order_by_exclude_fields parameter can be declared" if djangoType._meta.order_by_only_fields != "__all__": exclude_fields = [ name for name, field in model_fields if name not in djangoType._meta.order_by_only_fields ] else: exclude_fields = [ name for name, field in model_fields if name in djangoType._meta.order_by_exclude_fields ] model_fields = [(name, field) for name, field in model_fields if name not in exclude_fields] without = "" if exclude is not None or only is not None: assert not (exclude is not None and only is not None ), "Only one of only or exclude parameter can be declared" if only is not None: exclude_fields = [ name for name, field in model_fields if name not in only ] elif exclude is not None: exclude_fields = [ name for name, field in model_fields if name in exclude ] model_fields = [(name, field) for name, field in model_fields if name not in exclude_fields] exclude_fields.sort() without = "without_" + "_".join(exclude_fields) + "_" input_type_name = "{}_{}_{}input".format(model.__name__, input_flag, without) input_type_name = to_camel_case(input_type_name) input_type = registry.get_type_for_input(input_type_name) if input_type: return input_type def embeded_list_fields(): return graphene.List( convert_model_to_input_type(model, input_flag="where", registry=registry)) def embeded_field(): return graphene.Field( convert_model_to_input_type(model, input_flag="where", registry=registry)) items = OrderedDict() if input_flag == "create_nested_many": items["create"] = graphene.List( convert_model_to_input_type(model, input_flag="create", registry=registry)) items["connect"] = graphene.List( convert_model_to_input_type(model, input_flag="where", registry=registry)) elif input_flag == "update_nested_many": items["create"] = graphene.List( convert_model_to_input_type(model, input_flag="create", registry=registry)) items["delete"] = graphene.List( convert_model_to_input_type(model, input_flag="where", registry=registry)) items["connect"] = graphene.List( convert_model_to_input_type(model, input_flag="where", registry=registry)) items["disconnect"] = graphene.List( convert_model_to_input_type(model, input_flag="where", registry=registry)) elif input_flag == "create_nested": items["create"] = graphene.Field( convert_model_to_input_type(model, input_flag="create", registry=registry)) items["connect"] = graphene.Field( convert_model_to_input_type(model, input_flag="where", registry=registry)) elif input_flag == "update_nested": items["create"] = graphene.Field( convert_model_to_input_type(model, input_flag="create", registry=registry)) items["delete"] = Boolean() items["connect"] = graphene.Field( convert_model_to_input_type(model, input_flag="where", registry=registry)) items["disconnect"] = Boolean() else: if input_flag == "where": items["id"] = IdFilter() for name, field in model_fields: if name == "id" and input_flag in ["create", "update", "where"]: continue converted = convert_django_field_with_choices( field, input_flag=input_flag, registry=registry) items[name] = converted if "create" in input_flag or "update" in input_flag: for name, field in djangoType._meta.input_extend_fields: items[name] = field if input_flag == "where": items["OR"] = graphene.Dynamic(embeded_list_fields) items["AND"] = graphene.Dynamic(embeded_list_fields) items["NOT"] = graphene.Dynamic(embeded_field) ret_type = type( input_type_name, (graphene.InputObjectType, ), items, ) registry.register_input(input_type_name, ret_type) return ret_type
def get_related_model_type(cls, field: models.Field) -> graphene.Dynamic: model = field.related_model return graphene.Dynamic(cls.get_field_resolver(model, many=type(field) not in cls.foreign_fields))
class StatementGraph(SQLAlchemyObjectType): class Meta: model = Statement text = graphene.String() textversions = graphene.Field(TextVersionGraph) supports = ArgumentGraph.plural() rebuts = ArgumentGraph.plural() undercuts = ArgumentGraph.plural() def resolve_textversions(self, info, **kwargs): return resolve_field_query({ **kwargs, "statement_uid": self.uid }, info, TextVersionGraph) def resolve_text(self: Statement, info): return self.get_text() def resolve_supports(self, info, **kwargs): return resolve_list_query( { **kwargs, "is_supportive": True, "conclusion_uid": self.uid }, info, ArgumentGraph) def resolve_rebuts(self, info, **kwargs): return resolve_list_query( { **kwargs, "is_supportive": False, "conclusion_uid": self.uid }, info, ArgumentGraph) def resolve_undercuts(self, info, **kwargs): # Query for all arguments attacking / supporting this statement sq = DBDiscussionSession.query(Argument.uid) \ .filter(Argument.conclusion_uid == self.uid, Argument.is_disabled == False) \ .subquery() # Query for all arguments, which are attacking the arguments from the query above. return ArgumentGraph.get_query(info) \ .filter_by(**kwargs) \ .filter( Argument.is_disabled == False, Argument.argument_uid.in_(sq) ) @staticmethod def singular(): return graphene.Field(StatementGraph, uid=graphene.Int(), is_position=graphene.Boolean(), is_disabled=graphene.Boolean()) @staticmethod def plural(): return graphene.List(StatementGraph, is_position=graphene.Boolean(), is_disabled=graphene.Boolean()) flat_statements_below = graphene.Dynamic(lambda: graphene.NonNull( StatementGraph.plural(), description= "Returns all texts from the statements in the tree below this statement" )) def resolve_flat_statements_below(self: Statement, _info): return self.flat_statements_below()