Which part of script makes JSON API return as array of objects in Django Rest Framework? - django-rest-framework

I implemented JSON API using ListAPIView. I just wonder which part of script makes JSON API to return as array of objects like below.
Can anyone point out and where should I change if I want to return just object?
Views.py
class summaryData(generics.ListAPIView):
serializer_class=summarySerializer
def get_queryset(self):
pk=self.kwargs['pk']
key=self.kwargs['keyword']
return summary.objects.filter(html__pk=pk).filter(keyword=key)
serializer.py
class strToJson(serializers.CharField):
def to_representation(self,value):
x=JSON.loads(value)
return x
class summarySerializer(serializers.ModelSerializer):
project=serializers.CharField(read_only=True,source="html.project")
version = serializers.CharField(read_only=True, source="html.version")
json = strToJson()
class Meta:
model=summary
fields=('project','version','json')

Because you want to get a single object, you want to use a RetrieveAPIView (or a variant). This would correspond to the "summary detail" url (as opposed to the "summary list" url, where ListAPIView makes sense). This is what it should roughly look like:
path-to-your/urls.py:
urlpatterns = [
...
url(r'^summaries/(?P<pk>[0-9]+)/$', views.SummaryDetail.as_view()),
]
path-to-your/views.py:
class SummaryDetail(generics.RetrieveAPIView):
queryset = Summary.objects.all()
serializer_class = summarySerializer

you are using generics.ListAPIView this means your intention is to get list of all objects so generics.ListAPIView gives array of objects. check this blog for ref

Related

What is the meaning in single model instance in RetrieveAPIView in drf

What is the meaning of single model instance in RetrieveAPIView in Django REST framework.And how can we use? And will it look?
As my understanding:
single model instance: Example we use RetrieveAPIView. If we read the method that handle requests to that class, that is:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
We can see self.get_object(), it will build a Queryset to get object instance from our model class using method get(), or rather using get_object_or_404(). As we know, get() will only take 1 object instance, or it called single model instance.
Example Book.objects.get(id=10).
For more detail, we can open this link. Start from def retrieve then read also get_object().
Likewise on DestroyAPIView and UpdateAPIView.
collection of model instances: When build a Queryset, it using all() or filter(), according to what we wrote in queryset = .... or we override in def get_queryset. Example: Book.objects.all(). See this link and read def list.
CMIW.

Django REST Framework: "NoneType object is not iterable" error when trying to use serializer.data construct from within Serializer Method Field?

I am using a model that consists of many fields. There is one field that is a property, and it returns an instance of a model. Something like the following:
class A(Model):
#property
def last_obj(self):
# Returns an object
The issue I'm having is that this property can return 2 different Model types. It can either return an object of type one, or an object of type two. This creates complications in the serializer. I have a serializer that consists of nested serializers. The two objects are similar enough that one serializer can be used over the other, but then the fields unique to them are not serialized.
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
last_obj = SerializerOne() #This works, but not viable because of what I stated above
So my solution to be able to dynamically call the correct serializer, was to conditionally serialize the property within a serializer method field:
class A_Serializer(Serializer):
class SerializerOne(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.one
exclude = ('id')
base_name = 'one'
class SerializerTwo(CustomSerializer):
#Serializes certain fields in custom manner
class Meta:
model = models.two
exclude = ('id')
base_name = 'two'
def get_last_obj(self, instance):
if (isinstance(instance.last_obj, models.one)):
return self.SerializerOne(instance.last_obj).data
else:
return self.SerializerTwo(instance.last_obj).data
last_obj = SerializerMethodField() #Does not work
However, this solution creates the error "NoneType Object is not iterable" and it happens at
super(ReturnDict, self).__init__(*args, **kwargs) in rest_framework/utils/serializers_helpers.py in init which causes the error at return ReturnDict(ret, serializer=self) in rest_framework/serializers.py in data
I do not understand why calling a nested serializer like obj = Serializer() works, but calling the serializer explicitly like obj = Serializer(instance).data does not work in this situation. Can anyone figure out what I have been doing wrong? Thank you.
I have found out from here that when working with hyperlinked relations (which in my case was the CustomSerializer that SerializerOne and SerializerTwo were inheriting from), you must pass the request object through context. The reason why obj = Serializer() works, but obj = Serializer(instance).data does not work is that in the former, the request object is automatically added through context through DRF. While in the latter, it is being explicitly called so you must pass context with the request object manually. So for me to get it working, I did:
return self.SerializerOne(instance.last_obj, context={'request': self.context['request']}).data
inside the serializer method field.

DRF how to return list filtered by lookup w/ custom router and modelviewset

I tried to search for answers as much as I can but I still don't know how to achieve my goal here.
My goal:
I need two api endpoints, one returns a list filtered by a lookup fields, and another returns an obj filtered by another field, both using GET method. For example:
<ip>/api/books/bycategory/{category_lookup}/ This endpoint will return a list of books filtered by a category
<ip>/api/books/byid/{id_lookup}/ This returns one book matches the specified id (not pk)
Since there's no built-in router that suits my needs here because the built-in ones don't provide url pattern with lookup that returns a list, so I figured I need to have a custom router of my own, so here's what I have:
class CustomRouter(routers.SimpleRouter):
routes = [
routers.DynamicRoute(
url=r'^{prefix}/{url_path}/{lookup}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
)
]
router = CustomRouter()
router.register('books', BookViewSet)
and my serializer:
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = BookKeeper
fields = '__all__'
Right until here I think I'm on the right track, but when it comes to the view, thats where i can't quite figure out. Right now I only have this incomplete viewset:
class BookViewSet(viewsets.ReadOnlyModelViewSet):
queryset = BookKeeper.objects.all()
serializer_class = BookSerializer
#action(detail=True)
def bycategory(self, request):
lookup_field = 'category'
#action(detail=True)
def byid(self, request):
lookup_field = 'id'
My first question here is I "think" {url_path} in the router url matches the method name with #action specified in the viewset somehow and that how they are connected, am I correct?
Second question is how do I use {lookup} value in the view?
Third, what's the lookup_field for if I'm to use like:
def bycategory(self, request):
return Response(BookKeeper.objects.filter(category=<lookup_value>))
Lastly what should my viewset be like anyway?
Any input will be appreciated.
Second question is how do I use {lookup} value in the view?
You need two lookup_fields for the same set. You can do that by a custom Mixin class. But in four case, it is better not to use routers but custom urls, so edit like this:
# views.py
class BookViewSet(viewsets.ReadOnlyModelViewSet):
queryset = BookKeeper.objects.all()
serializer_class = BookSerializer
#action(detail=True)
def bycategory(self, request, category):
# do filtering by category
print(category)
#action(detail=True)
def byid(self, request, book_id):
# do filtering by book_id
print(book_id)
# urls.py
get_by_id = views.BookViewSet.as_view(
{
'get': 'byid'
}
)
get_by_category = views.BookViewSet.as_view(
{
'get': 'bycategory'
}
)
urlpatterns += [
url(
r'^api/books/byid/(?P<book_id>[0-9a-f-]+)/',
get_by_id,
name='get-by-id'
),url(
r'^api/books/bycategory/(?P<category>[0-9a-f-]+)/',
get_by_category,
name='get-by-category'
)
]

How to use a custom id with Graphene and Relay?

I've implemented graphql and I'm migrating to relay. I already have a uuid for every table and that is named 'id'. And my application I found this github thread that talks about possibly changing the spec but it feels like a rabbit hole.
Is there a simple way that I can use my own custom id with relay?
If you've already implemented a default relay endpoint then you should have some
TableNameNode classes that have a Meta nested class, and a seperate Query
class.
class ExampleTableNameNode(DjangoObjectType):
class Meta:
model = ExampleTableName
interface = (relay.Node,)
class Query(object):
example_table_name = relay.Node.Field(ExampleTableNameNode)
all_example_table_names = DjangoFilterConnectionField(ExampleTableNameNode)
def resolve_example_table_name(self, info, **kwargs):
pass
def resolve_all_example_table_names(self, info, **kwargs):
pass
The interface = (relay.Node,) is what defines:
How the ids are being generated
How they are used to fetch data
If we create a relay.Node subclass that redefines these two features then we can use our custom ids.
class CustomNode(relay.Node):
class Meta:
name = 'Node'
#staticmethod
def to_global_id(type, id):
#returns a non-encoded ID
return id
#staticmethod
def get_node_from_global_id(info, global_id, only_type=None):
model = getattr(Query,info.field_name).field_type._meta.model
return model.objects.get(id=global_id)
Here we implemented two functions, to_global_id, and get_node_from_global_id.
The line model = ... is a bit of magic to go from the graphql query table name
to the actual model. If that doesn't work you'll just need to make a dictionary
to go from something like example_table_name to the actual ExampleTableName
django model.
Once you do that you'll have to replace the two references to relay.Node with
CustomNode like so.
class ExampleTableNameNode(DjangoObjectType):
class Meta:
model = ExampleTableName
interface = (CustomNode,)
class Query(object):
example_table_name = CustomNode.Field(ExampleTableNameNode)
all_example_table_names = DjangoFilterConnectionField(ExampleTableNameNode)
def resolve_example_table_name(self, info, **kwargs):
pass
def resolve_all_example_table_names(self, info, **kwargs):
pass
The answer is in the graphene docs. I read them when I was implementing
graphene and relay but there is so much to learn at once that it's easy to read
through custom node section and not remember later that you need to do a custom
node solution.

get_queryset method and ViewSets in django rest framework

I am doing exactly as the example states
here is my method
class FeedViewSet(viewsets.ModelViewSet):
model = Feed
serializer_class = FullFeedSerializer
def get_queryset(self):
user = request.user
queryset = Feed.objects.get_nearby(user)
return queryset
when i execute it, it says request not defined .. which actually isn't. the example at the rest framework's site also haven't defined request. what am i doing wrong?
The request object is available (on either REST framework's class based views, or Django's standard class based views) as self.request. You're missing the self. part of that.

Resources