A simple query but Graphene-Django is returning null values - graphql

I am trying to make a simple query with graphene-django but i can not get the DB, it gives me null.
I think the code is ok, what is going wrong, I am working for hours on it.
Do you have any idea, what it is?
Thanks in advance
import graphene
from graphene_django.types import DjangoObjectType, ObjectType
from myProject.models import Times
class TimesType(DjangoObjectType):
class Meta:
model=Times
fields="__all__"
class Query(ObjectType):
today_times = graphene.Field(TimesType, id=graphene.ID())
all_times = graphene.List(TimesType)
def resolve_todaytimes(self, info, id=None):
return Times.objects.get(pk=id)
def resolve_alltimes(root, info, **kwargs):
return Times.objects.all()
schema = graphene.Schema(query=Query, mutation=Mutation)
query {todayTimes(id:"1029"){id}}
{
"data": {
"todayTimes": null
}
}

The resolver method should be named in resolve_<FieldName> format
class Query(ObjectType):
today_times = graphene.Field(TimesType, id=graphene.ID())
all_times = graphene.List(TimesType)
def resolve_today_times(self, info, id=None): # not `resolve_todaytimes`
return Times.objects.get(pk=id)
def resolve_all_times(root, info, **kwargs): # not `resolve_alltimes`
return Times.objects.all()
Alternatively, you can use the resolver parameter to set the callable resolver as,
def resolve_todaytimes(self, info, id=None):
return Times.objects.get(pk=id)
def resolve_alltimes(root, info, **kwargs):
return Times.objects.all()
class Query(ObjectType):
today_times = graphene.Field(
TimesType,
id=graphene.ID(),
resolver=resolve_todaytimes
)
all_times = graphene.List(
TimesType,
resolver=resolve_alltimes
)

Related

SerializerMethodField doesn't work on DRF

Here is the simplified verison of the Serializer what I have:
class CommentSerializer(serializers.HyperlinkedModelSerializer):
def __init__(self, *args, **kwargs):
init = super().__init__(*args, **kwargs)
return init
username = serializers.ReadOnlyField(source='user.username')
user_id = serializers.ReadOnlyField(source='user.id')
user = UserSerializer(read_only=True)
url_field_name = 'url_api'
# and more definitions
content_type_id = serializers.IntegerField()
site_id = serializers.SerializerMethodField('_siteId')
def _siteId(self, threadedcomment):
site_id = settings.SITE_ID
return site_id
class Meta:
model = ThreadedComment
fields = ('url_api','url','id','title','tree_path','comment','submit_date','submit_date_unix','submit_date_humanized','root_id','is_removed',
'parent_id','last_child_id','newest_activity','depth','username','user_id','object_pk','content_type_id','user',
'site_id',
)
read_only_fields = ('id','title','tree_path','submit_date','root_id','is_removed',
'parent_id','last_child_id','newest_activity','depth','username','user_id',
# 'site_id',
)
class CommentViewSet(viewsets.ModelViewSet):
queryset = ThreadedComment.objects.all().annotate().all()
serializer_class = CommentSerializer
permission_classes = []
filter_backends = [filters.OrderingFilter]
def filter_queryset(self, queryset):
return queryset
def list(self, request):
return super().list(request)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
# site_id = settings.SITE_ID
# serializer.save(user=self.request.user, site_id=site_id, )
return super().perform_create(serializer)
Now I make an http post request on the api as i.e.:
axios.post('/api/comments/', {
"comment":"test.",
"object_pk":34,
"content_type_id":12,
},
It shows me an error:
500 (Internal Server Error)
IntegrityError: NOT NULL constraint failed: django_comments.site_id
The problem (a pure question) is, why the SerializerMethodField doesn't work? I put a breakpoint on the site_id = settings.SITE_ID line, but it doesn't hit, which means the line hasn't even executed.
I also tried putting a set of lines (the commented lines) on perform_create, reading a SO post, but the result is the same, the same error, NOT NULL constraint failed.
I'm certainly passing a value, but it shows it's a null value, saying nothing is passed, what this means? What am I donig wrong here? Thanks.

How to do 'or' when applying multiple values with the same lookup?

By referring to this article, I was able to implement the method of and search.
Django-filter with DRF - How to do 'and' when applying multiple values with the same lookup?
I want to know how to do or search for multiple keywords using the same field. How can I implement it?
Here is the code:
from rest_framework import viewsets
from django_filters import rest_framework as filters
from .serializers import BookInfoSerializer
from .models import BookInfo
class MultiValueCharFilter(filters.BaseCSVFilter, filters.CharFilter):
def filter(self, qs, value):
# value is either a list or an 'empty' value
values = value or []
for value in values:
qs = super(MultiValueCharFilter, self).filter(qs, value)
return qs
class BookInfoFilter(filters.FilterSet):
title = MultiValueCharFilter(lookup_expr='contains')
# title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) -> get an error
class Meta:
model = BookInfo
fields = ['title']
class BookInfoAPIView(viewsets.ReadOnlyModelViewSet):
queryset = BookInfo.objects.all()
serializer_class = BookInfoSerializer
filter_class = BookInfoFilter
if I set conjoined=False like this title = MultiValueCharFilter(lookup_expr='contains', conjoined=False) get an error __init__() got an unexpected keyword argument 'conjoined'
Django 3.2.5
django-filter 2.4.0
djangorestframework 3.12.4
Python 3.8.5
You can try to modify the queryset returned from MultiValueCharFilter
and combine values with operator.
example:
import operator
from functools import reduce
class MultiValueCharFilter(BaseCSVFilter, CharFilter):
def filter(self, qs, value):
expr = reduce(
operator.or_,
(Q(**{f'{self.field_name}__{self.lookup_expr}': v}) for v in value)
)
return qs.filter(expr)
class BookInfoFilter(filters.FilterSet):
title = MultiValueCharFilter(lookup_expr='contains')
class Meta:
model = BookInfo
fields = ['title']
You can try to create a new Class called ListFilter like this:
from django_filters.filters import Filter
from django_filters.conf import settings as django_filters_settings
from django.db.models.constants import LOOKUP_SEP
class ListFilter(Filter):
"""
Custom Filter for filtering multiple values.
"""
def __init__(self, query_param: str, *args, **kwargs):
"""
Override default variables.
Args:
query_param (str): Query param in URL
"""
super().__init__(*args, **kwargs)
self.query_param = query_param
self.distinct = True
def filter(self, qs, value):
"""
Override filter method in Filter class.
"""
try:
request = self.parent.request
values = request.query_params.getlist(self.query_param)
# Remove empty value in array
values = list(filter(None, values))
except AttributeError:
values = []
# Filter queryset by using OR expression when lookup_expr is 'in'
# Else filter queryset by using AND expression
if values and self.lookup_expr == 'in':
return super().filter(qs, values)
for value in set(values):
predicate = self.get_filter_predicate(value)
qs = self.get_method(qs)(**predicate)
return qs.distinct() if self.distinct else qs
def get_filter_predicate(self, value):
"""
This function helps to get predicate for filtering
"""
name = self.field_name
if (
name
and self.lookup_expr != django_filters_settings.DEFAULT_LOOKUP_EXPR
):
name = LOOKUP_SEP.join([name, self.lookup_expr])
return {name: value}
My class above supports filter nested fields using both OR and AND expressions. E.g. about filter title field using OR expression:
class BookInfoFilter(filters.FilterSet):
title = ListFilter(query_param='title')
class Meta:
model = BookInfo
fields = ['title']

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'):

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.

How can I force ModelForm to use specific database?

I have this ModelForm:
class ClienteForm(ModelForm):
class Meta:
model = Pessoa
def __init__(self, *args, **kwargs):
vUserProfile = kwargs.pop('vUserProfile', None)
super(ClienteForm, self).__init__(*args, **kwargs)
How can I force to use a specific database?
I can't use db router, because the "specific" database is setting in my User profile, and I don't know how to get UserProfile in db-router class..
I know I can use in ClienteForm.save(using=XXX), but when I try ClienteForm.is_valid I got error, because django is try to use "default" database.
thanks
Reply my own question...
The only way to get this right is make one middleware to get database name and work with locals, like this:
File: middleware.py
from threading import local
from django.contrib.sessions.models import Session
from django.contrib.auth.models import User
from web_core.models import UserProfile
my_local_global = local()
class CustomerMiddleware(object):
def process_request(self, request):
my_local_global.database_name = get_database_name(request)
def get_database_name(request):
session_key = request.session.session_key
try:
session = Session.objects.get(session_key=session_key)
uid = session.get_decoded().get('_auth_user_id')
user = User.objects.get(pk=uid)
profile = UserProfile.objects.get(pk=uid)
if profile:
return profile.dbname
else:
return None
except:
return None
after this, add middleware.py in your settings.py:
MIDDLEWARE_CLASSES = (
(..)
'middleware.CustomerMiddleware',
)
to finish, make one more file to get db router:
File: authrouter:
class PadraoRouter(object):
def db_for_read(self, model, **hints):
from middleware import my_local_global
return my_local_global.database_name
def db_for_write(self, model, **hints):
from middleware import my_local_global
return my_local_global.database_name
def allow_relation(self, obj1, obj2, **hints):
return None
def allow_syncdb(self, db, model):
return True
class AuthRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label == 'auth':
return 'auth_db'
if model._meta.app_label == 'sessions':
return 'auth_db'
if model._meta.app_label == 'web_core':
return 'auth_db'
return None
def db_for_write(self, model, **hints):
if model._meta.app_label == 'auth':
return 'auth_db'
if model._meta.app_label == 'sessions':
return 'auth_db'
if model._meta.app_label == 'web_core':
return 'auth_db'
return None
def allow_relation(self, obj1, obj2, **hints):
if obj1._meta.app_label == 'auth' or\
obj2._meta.app_label == 'auth':
return True
if obj1._meta.app_label == 'sessions' or\
obj2._meta.app_label == 'sessions':
return True
if obj1._meta.app_label == 'web_core' or\
obj2._meta.app_label == 'web_core':
return True
return None
def allow_syncdb(self, db, model):
if db == 'auth_db':
return model._meta.app_label == 'auth'
elif model._meta.app_label == 'auth':
return False
return None
NOTE: I need to put import in each def, because Django 1.5.1 has a bug, if you put import into top of file.. cycle imports..
after this, change again your settings.py to add the router:
DATABASE_ROUTERS = ['authrouter.AuthRouter',
'authrouter.PadraoRouter']
Remember
I make this way, because I have one database, only for auth.. each user can access a different database, depending what is save in dbname field.
If you have other solution, please let's me know!
Thanks to everybody.

Resources