Graphene-Django create multiple instances - graphql

Let's say I've got a models.py with two tables:
class Category(models.Model):
cat = models.CharField(max_length=100)
class Thing(models.Model):
desc = models.CharField(max_length=100)
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
and my schemas as follows:
class ThingType(DjangoObjectType):
class Meta:
model = Thing
class FindThing(graphene.ObjectType):
things = graphene.List(
ThingType,
search=graphene.String(),
thing=graphene.ID(),
)
def resolve_things(self, info, thing=None, search=None, **kwargs):
qs = Thing.objects.all()
if search:
filter = (
Q(desc__icontains=search)
)
qs = qs.filter(filter)
if thing:
qs = qs.filter(id=thing)
return qs
class CreateThing(graphene.Mutation):
id = graphene.Int()
desc = graphene.String()
category = graphene.Field(FindCategory)
class Arguments:
desc = graphene.String()
category = graphene.Int()
def mutate(self, info, desc, category):
thing = Thing(
desc=desc,
category=Category.objects.get(id=category)
)
thing.save()
return CreateThing(
id=thing.id,
desc=thing.desc,
category=thing.category_id
)
class CategoryType(DjangoObjectType):
class Meta:
model = Category
class GetCategory(graphene.ObjectType):
category = graphene.List(
CategoryType,
category=graphene.String(),
)
def resolve_category(self, info, category=None, **kwargs):
qs = Category.objects.all()
if category:
get = (
Q(category__contains=category)
)
qs = qs.get(get)
return qs
class FindCategory(graphene.ObjectType):
categories = graphene.List(
CategoryType,
search=graphene.String(),
cat=graphene.ID(),
)
def resolve_categories(self, info, cat=None, search=None, **kwargs):
qs = Category.objects.all()
if search:
filter = (
Q(cat__icontains=search)
)
qs = qs.filter(filter)
if cat:
qs = qs.filter(id=cat)
return qs
class CreateCategory(graphene.Mutation):
id = graphene.Int()
cat = graphene.String()
desc = graphene.String()
class Arguments:
cat = graphene.String()
desc = graphene.String()
def mutate(self, info, cat, desc):
category = Category(
cat=cat
)
category.save()
thing = Thing(
desc=desc,
category_id=category.id
)
thing.save()
return CreateCategory(
id=category.id,
cat=category.cat,
desc=thing.desc,
)
I've managed to create a schema where one can create a new category that already links to a newly created single thing:
mutation createCategory{
createCategory(cat:"cat7", desc:"defg"){
id
cat
desc
}
}
Is it possible to create a CreateCategory django-graphene schema where one can create a category with multiple additional new things?

You could allow the CreateCategory mutation to accept a list of descriptions for multiple things:
CreateCategory:
descs = graphene.List(String)
class Arguments:
descs = graphene.List(String)
and then loop over this list inside the mutate function:
new_things = []
for desc in descs:
thing = Thing(
desc=desc,
category_id=category.id
)
thing.save()
new_things.append(thing.desc)
return CreateCategory(
id=category.id,
cat=category.cat,
descs=new_things
)

Related

Flask-msearch in many to many relationship

I'm trying to create a full text search using . It works well in one table. With this, my database has a many-to-many relationship:
from flask_msearch import Search
search = Search()
app = Flask(__name__)
tasks_topics= db.Table('tasks_topics',
db.Column('task_id', db.Integer, db.ForeignKey('tasks.id')),
db.Column('topics_id', db.Integer, db.ForeignKey('topics.id'))
)
class Tasks(db.Model):
__tablename__ = 'tasks'
__searchable__ = ['task','id','topics.name'] # these fields will be indexed by whoosh
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
url = db.Column(db.String(120))
task = db.Column(db.Text)
topics = db.relationship('Topics',
secondary=tasks_topics,
back_populates="tasks")
class Topics(db.Model):
__tablename__ = 'topics'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(140))
parent = db.Column(db.Integer, default=0)
# связи
tasks = db.relationship('Tasks',
secondary=tasks_topics,
back_populates="topics")
search.create_index(Tasks)
results = Tasks.query.msearch('Find x',fields=['task']).all()
print(results)
In documentation flask-msearch example shows one go many relationship. How should I solve this problem?

How to get item name instead of item id in django restframework (foreignkey case)

I am trying to get itemwise inventory desired result is like this
[
{
'item' : shoes,
'total_unit' : 134
},
{
'item': sneaker,
'unit': 100
}
]
but I am getting result like
[
{
"item": 5,
"unit": 134
},
{
"item": 4,
"unit": 100
}
]
I want to get item name instead of id
Models.py
class Item(models.Model):
item_name = models.CharField(max_length=50)
srn_code = models.CharField(max_length=20,unique=True)
category = models.ForeignKey(Category,related_name='categories',on_delete=models.CASCADE)
def __str__(self):
return self.item_name
class Transaction(models.Model):
category = models.ForeignKey(Category,on_delete=models.CASCADE)
item = models.ForeignKey(Item,on_delete=models.CASCADE)
size = models.CharField(blank=True,max_length=20)
unit = models.IntegerField()
unit_price = models.DecimalField(decimal_places=2,max_digits=20)
supplier = models.ForeignKey(Supplier,on_delete=models.CASCADE)
tran_date = models.DateField(auto_now=False,auto_created=False,blank=False)
created_date = models.DateTimeField(auto_now=True)
def __str__(self):
return f"{self.item.item_name} {self.unit}"
serializer.py
class InventorySerializer(serializers.ModelSerializer):
item = serializers.CharField(read_only=True)
total_unit = serializers.IntegerField(read_only=True)
class Meta():
model = Transaction
fields = ['item','total_unit']
views.py
class InventoryModelViewSet(ModelViewSet):
queryset = Transaction.objects.all()
serializer_class = InventorySerializer
def get_queryset(self):
return Transaction.objects.values('item').annotate(
total_unit = Sum('unit')
).order_by('item')
Thanks for posting my question, I have sorted out my question, my solution is below, if anybody needs or gets query like this, then its might be helpful.
I have declared a method in serializers class for get item_name from another table
class InventorySerializer(serializers.ModelSerializer):
item = serializers.CharField(read_only=True)
total_unit = serializers.IntegerField(read_only=True)
item_name = serializers.SerializerMethodField()
class Meta():
model = Transaction
fields = ['item','item_name','total_unit']
def get_item_name(self, obj):
item_obj = Item.objects.filter(id=obj['item']).first()
item_name = model_to_dict(item_obj)
return item_name['item_name']
`

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()

Filtering with DjangoRestMultipleModels

According to the relevant documentation "Django Rest Frameworks default Filter Backends work out of the box" with DjangoRestMultipleModels. So, I'd expect the following code to work:
class AllModelSummary(MultipleModelAPIView):
filter_backends = (SearchFilter,DjangoFilterBackend,)
search_fields = ('name','description',)
#filter_fields = ('is_foo','is_bar',) # Everything breaks when this is uncommmented
flat = True
def get_queryList(self):
queryList = (
(Foo.objects.all(), FooSerializerMiniList),
(Bar.objects.all(), BarSerializerMiniList)
)
return queryList
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
Here's an example of one of those serializers:
class BarSerializerMiniList(serializers.ModelSerializer):
is_foo = serializers.SerializerMethodField()
is_bar = serializers.SerializerMethodField()
def get_is_foo(self,obj):
return False
def get_is_bar(self,obj):
return True
class Meta:
model = Bar
fields = ('pk','name','description','is_bar','is_foo')
The search fields do exactly what they're supposed to do, but if I define filter_fields in the API then I am greeted by this:
'Meta.fields' contains fields that are not defined on this FilterSet: is_foo, is_bar
Any suggestions as to what might be going wrong here would be welcome.
In the end I had to work around the issue as follows:
class AllModelSummary(MultipleModelAPIView):
...
def get_querylist(self):
types = self.request.query_params['types']
queryList = ()
if types:
for t in types.split(','):
if t == 'foo':
queryList = queryList + ((Foo.objects.all(), FooSerializerMiniList),)
elif t == 'bar':
queryList = queryList + ((Bar.objects.all(), BarSerializerMiniList),)
else:
queryList = (
(Foo.objects.all(), FooSerializerMiniList),
(Bar.objects.all(), BarSerializerMiniList),
)
return queryList
Then, appending ?types=foo,bar to the URL does the trick.

Django get_initial not working

Django get_initial is not populating product field in the form. I am expecting a drop down, with the queryset results as defined in the get_initial overridden function.
class PurchaseRequestDetailForm(forms.ModelForm):
class Meta:
model = PurchaseRequestDetail
fields = ["product", "variations", "quantity", "fulfilled", "vat", "discount", "surcharges", "active"]
exclude = ("purchase_request", )
class PurchaseRequestDetailCreateView(CreateView):
model = PurchaseRequestDetail
form_class = PurchaseRequestDetailForm
template_name = "inventory/purchaserequestdetail_form.html"
def get_pr_obj(self):
pr_id = self.request.session["pr_id"]
return PurchaseRequest.objects.get(id=pr_id)
def get_initial(self):
initial = super(PurchaseRequestDetailCreateView, self).get_initial()
try:
pr_obj = self.get_pr_obj()
initial["product"] = pr_obj.vendor.vendors_products.all()
except KeyError:
pass
self.form_class(initial)
return initial
template:
<td>{{ form.product|css_class:"form-control" }}</td>
An easy way to set a ModelChoiceField queryset is to set the field attribute in the form init();
class PurchaseRequestDetailForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
# Get initial data passed from the view
self.product = None
if 'product' in kwargs['initial']:
self.product = kwargs['initial'].pop('product')
super(PurchaseRequestDetailForm, self).__init__(*args, **kwargs)
self.fields['product'].queryset = self.product
class Meta:
model = PurchaseRequestDetail
fields = ["product", "variations", "quantity", "fulfilled", "vat", "discount", "surcharges", "active"]
exclude = ("purchase_request", )
You should hook in to get_form_kwargs from ModelFormMixin to pass your data to the form.
class PurchaseRequestDetailCreateView(CreateView):
model = PurchaseRequestDetail
form_class = PurchaseRequestDetailForm
template_name = "inventory/purchaserequestdetail_form.html"
def get_pr_obj(self):
pr_id = self.request.session["pr_id"]
return PurchaseRequest.objects.get(id=pr_id)
def get_form_kwargs(self):
"""
Returns the keyword arguments for instantiating the form.
"""
kwargs = super(PurchaseRequestDetailCreateView, self).get_form_kwargs()
kwargs.update(
{'initial':
{'product': pr_obj.vendor.vendors_products.all()}
}
)
return kwargs

Resources