How to get models schema (type_defs) in form of string? - graphene-python

Example Code
I have the following model.
class Post(Base):
__tablename__ = "post"
id = Column(Integer, primary_key=True, index=True)
title = Column(String)
author = Column(String)
content = Column(String)
time_created = Column(DateTime(timezone=True), server_default=func.now())
with this schema
class PostSchema(SQLAlchemyObjectType):
class Meta:
model = Post
Problem
How to to get the graphql schema definition in form of string like this
"""
type Post{
id: ID
title: string!
author: String!
content: String!
time_created: Int!
}
"""
I tried this, but there is not type_defs option.
graphene.Field(PostModel).type_defs

One option you can use is to introspect your schema not just the object type. Suppose you have a graphene schema called schema,
from graphene_sqlalchemy import SQLAlchemyObjectType
class schema(SQLAlchemyObjectType):
class Meta:
model = MyModel
you can do something like:
schema_dict = schema.introspect()
This will return a dict version of your schema and the types that you want can be found here:
types = schema_dict["__schema"]["types"]
Unfortunately, this returns a list of all object types so you might need to process it to narrow down what you want with something like:
list(filter(lambda types: types['name'] == 'PostSchema', types))
alternative way
def make_schemas(model):
d = """
"""
for i, key in model.__table__.columns.items():
x = str(key.type.python_type)
x = x.replace("<class '", '')
x = x.replace("'>", '')
x = x.title()
if x in ['Str', 'Datetime.Datetime']:
x = 'String'
if x == 'Bool':
x = 'Boolean'
d += f"""
{i}: {x}
"""
y = f"""
type {model.__tablename__.title()} {{
{d}
}}
"""
return y

Related

Django TypeError get() argument after ** must be a mapping, not list

I'm creating a serializer for a model with a ManyToManyField (tag_id) that refers to a Tag table.
serializers.py
class CombinationSerializer(serializers.ModelSerializer):
# tag_id = serializers.PrimaryKeyRelatedField(queryset=Tag.objects.all(), source='tag', required=False, many=True)
tag_id = TagWithIdSerializer(many=True, required=False, write_only=False)
resource_id = serializers.PrimaryKeyRelatedField(queryset=Resource.objects.all(),
required=True,
source='resource',
write_only=False)
gameround_id = serializers.PrimaryKeyRelatedField(queryset=Gameround.objects.all(),
required=False,
source='gameround',
write_only=False)
user_id = serializers.PrimaryKeyRelatedField(queryset=CustomUser.objects.all(),
required=False,
source='user',
write_only=False)
class Meta:
model = Combination
depth = 1
fields = ('id', 'user_id', 'gameround_id', 'resource_id', 'tag_id', 'created', 'score')
def create(self, validated_data):
user = None
request = self.context.get("request")
if request and hasattr(request, "user"):
user = request.user
score = 0
combination = Combination(
user=user,
gameround=validated_data.get("gameround"),
resource=validated_data.get("resource"),
created=datetime.now(),
score=score
)
combination.save()
tag_data = validated_data.pop('tag_id', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag_id'] = tag
combination.tag_id.add(validated_data.get("tag_id"))
return combination
This is the problematic code:
tag_data = validated_data.pop('tag_id', None)
if tag_data:
tag = Tag.objects.get_or_create(**tag_data)[0]
validated_data['tag_id'] = tag
combination.tag_id.add(validated_data.get("tag_id"))
This is the JSON object I am trying to send in Postman:
{
"gameround_id": 2015685170,
"resource_id": 327888,
"tag_id": [{"id": 2001372884, "name": "combination", "language": "en"}]
}
What am I doing wrong here? Can this be done any other way?
The tag_id maps on a list of dictionaries, not a dictionary. You thus can enumerate over it:
tag_data = validated_data.pop('tag_id', None)
for tag_item in tag_data:
tag = Tag.objects.get_or_create(**tag_item)[0]
validated_data['tag_id'] = tag
since these are lists, it might be better however to make a list of tags in the validated data:
tag_data = validated_data.pop('tag_id', None)
validated_data['tag_id'] = [
Tag.objects.get_or_create(**tag_item)[0]
for tag_item in tag_data
]

Django GraphQL Mutation Updated, but no change in Database

I created an update mutation as follows, with django==3.1.4 and graphene==2.1.8 :
# models.py
class CustomUser(AbstractUser):
# email = models.EmailField()
firebase_id = models.CharField(max_length=50, null=True)
nickname = models.CharField(max_length=50, null=True)
name = models.CharField(max_length=20, null=True)
gender = models.IntegerField(choices=Gender, default=3)
phone = models.CharField(max_length=20, null=True)
birthday = models.DateField(default=datetime(2020,1,1))
address = models.CharField(max_length=200, null=True)
profile_image = models.ImageField(default='default-avatar.png', upload_to='users/',
null=True, blank=True)
class UpdateMember(graphene.Mutation):
class Arguments:
firebase_id = graphene.String(required=True)
nickname = graphene.String()
name = graphene.String()
gender = graphene.Int()
phone = graphene.String()
birthday = graphene.Date()
address = graphene.String()
profile_image = graphene.String()
class Meta:
exclude = ["password"]
member = graphene.Field(MemberType)
success = graphene.Boolean()
# #login_required
#staticmethod
def mutate(root, info, firebase_id, **kwargs):
success = False
member_instance = CustomUser.objects.get(firebase_id=firebase_id)
if member_instance:
print(member_instance)
success = True
for k, v in kwargs.items():
member_instance.k = v
member_instance.save()
return UpdateMember(member=member_instance, success=True)
else:
return UpdateMember(member=None, success=False)
Running GQL below:
mutation {
updateMember(
firebaseId:"777",
name:"JJJJ")
{
success
}
}
Response:
{
"data": {
"updateMember": {
"success": true
}
}
}
But I checked the database, it seems no change in it, I think .save() should have done the work persisting changes to database......
Creating Member works fine. Using PostgresQL
Could anyone figure out why?
There is several issues in your code:
You can not assign your model fields using string like that. See this thread
for k, v in kwargs.items():
member_instance.k = v
member_instance.save()
Currently your member_instance.k has nothing to do with variable k inside for loop.
firebase_id field should be unique.
Currently you call CustomUser.objects.get(firebase_id=firebase_id) which is risky because firebase_id is not unique field. This may lead Multiple objects error if you have more than one CustomUsers saved with same id. To fix it, just define:
class CustomUser(AbstractUser):
# email = models.EmailField()
firebase_id = models.CharField(max_length=50, unique=True)
...
To check if your member_instance has really updated. You can for example print out the values before saving it and run some test cases before final implementation. For example:
if member_instance:
print(member_instance)
success = True
for k, v in kwargs.items():
member_instance.k = v
print(member_instance.k)
print(k)
print(getattr(member_instance, k))
member_instance.save()

How to pass complex types as arguments in graphene Python

I'm trying to create a query that accepts a complex argument object like so:
class Pair(graphene.ObjectType):
x = graphene.Int()
y = graphene.Int()
class Pairs(graphene.ObjectType):
pairs = graphene.List(graphene.NonNull(graphene.Field(Pair, required=True)), required=True)
class Query(graphene.ObjectType):
endpoint = graphene.Field(ResultType, pairs=graphene.Argument(Pairs, required=True))
I'm invoking it as follows in testing:
client = graphene.test.Client(graphene.Schema(query=Query))
executed = client.execute(
"""query($pairs: Pairs!) {
endpoint(pairs: $pairs) {
[result type goes here]
}
}"""
Any thoughts on what may be wrong with this approach?
I was able to do with the code below
class SomeFilter(graphene.InputObjectType):
name = graphene.String()
class Query(graphene.ObjectType):
all_somes = graphene.List(Some, options=SomeFilter())
def resolve_all_somes(self, info, options=None):
if options:
if name := options.get('name'):

django elastic search giving TransportError 400 for model id large digit search

I am getting this error while I am searching large digit ,where my model has only interger field
class PurchaseDocument(DocType):
status = fields.IntegerField()
purchase_order_status = fields.IntegerField()
organization = fields.ObjectField(properties={
'pk': fields.IntegerField()
})
sales_return = fields.ObjectField(
properties={
'pk': fields.IntegerField()
}
)
class Meta:
model = Purchase
fields = [
'id',
'remarks',
'vouchar_no'
]
rebuild_from_value_list = True
related_models = [Purchase, Transaction, TransactionPurchase]
I have purchase model like this-
class Purchase(model.Models):
//something

Mutations - batch creation of objects

I want to use graphene to create many people in one go.
The document only mention the way to create one person like this:
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.String()
age = graphene.Int()
ok = graphene.Boolean()
person = graphene.Field(lambda: Person)
#staticmethod
def mutate(root, args, context, info):
person = Person(name=args.get('name'), age=args.get('age'), mobile=args.get('mobile'))
ok = True
return CreatePerson(person=person, ok=ok)
are there any ways to get it done?
Instead of using a mutation that creates a list of objects, you can also call a mutation that creates one objects multiple times in one GraphQL request. This is accomplished using GraphQL Aliases:
mutation {
c001: createPerson(
name: "Donald Duck"
age: 42
) {
id
}
c002: createPerson(
name: "Daisy Duck"
age: 43
) {
id
}
c003: createPerson(
name: "Mickey Mouse"
age: 44
) {
id
}
}
I can figure out a solution base on the answer of Jan Hančič
There is a type called graphene.InputObjectType to use in this case
The solution can be
class PersonInput(InputObjectType):
name = graphene.String()
age = graphene.Int()
class CreatePeople(graphene.Mutation):
class Input:
people = graphene.List(PersonInput)
people = graphene.List(lambda: Person)
#staticmethod
def mutate(root, args, context, info):
people = [Person.objects.create(name=person.name, age=person.age) for person in args.get('people')]
return CreatePeople(people=people)
Make your mutation input a list and return a list of created people. Something like this:
class CreatePerson(graphene.Mutation):
class Input:
name = graphene.List(graphene.String)
ok = graphene.Boolean()
people = graphene.List(Person)
#staticmethod
def mutate(root, args, context, info):
people = [Person(name=name) for name in args.get('name)]
ok = True
return CreatePerson(people=people, ok=ok)
Receive a list of input, create all instances and return them all
The model node/type should be like-
class UserType(DjangoObjectType):
class Meta:
model = User
interfaces = (CustomGrapheneNode, )
filter_fields = {}
only_fields = (
'name',
'email'
)
Define Input fields
class UserInput(graphene.InputObjectType):
name = graphene.String(required=True)
password = graphene.String(required=True)
Mutation class
class CreateUser(graphene.Mutation):
users = graphene.List(UserType)
class Input:
data = graphene.List(UserInput)
Output = graphene.List(UserType)
def mutate(self, info, data):
users = []
for item in data:
user = User.objects.create(name=data['name'],
password=data['password'])
users.append(user)
return users
make this mutation callable by main schema
class Mutation():
create_user = CreateUser.Field()
the Mutation Query view will be as -
mutation{
createUser(data:[{name:"john", password:"1234"},
{name:"john", password:"1234"}]) {
user{
name
}
}
}

Resources