Views.py -
#api_view(['GET'])
def view_items(request):
if request.query_params:
items = Item.objects.filter(**request.query_param.dict()) #error line
else:
items=Item.objects.all()
serializer=ItemSerializer(items,many=True)
if items:
return Response(serializer.data)
else:
return Response(status=status.HTTP_404_NOT_FOUND)
The **request.query_param should be changed into **request.query_params.
Let's say you want to filter by name.
#api_view(['GET'])
def view_items(request):
name = request.query_params.get("name", None)
if name:
items = Item.objects.filter(name=name)
I would suggest you to use django-filters, that's a better way to structure your code.
Create Item app
urls.py
app_name = "app-name"
router = SimpleRouter()
router.register("items", ItemViewSet, basename="item")
views.py
class ItemViewSet(views.ModelViewSet):
queryset = Item.objects.all()
serializer_class = ItemSerializer
filterset_class = ItemFilterSet
serializers.py
class ItemSerializer(serializers.ModelSerializer):
class Meta:
model = Item
fields = ("id", "name", "label")
filters.py
from django_filters import rest_framework as filters
class ItemFilterSet(filters.FilterSet):
class Meta:
model = Item
fields = ("name", "label")
Related
I am using django rest framework. I want to include the url of an action defined in a view in its serializer.
My serializers.py:
from rest_framework import serializers
class CommentSerializer(serializers.ModelSerializer):
"""Serializer for comments."""
class Meta:
model = Comment
fields = ["id", "item", "author", "content", "date_commented", "parent"]
class ItemDetailSerializer(serializers.ModelSerializer):
"""Serializer for items (for retrieving/detail purpose)."""
category = CategorySerializer(many=True, read_only=True)
media = MediaSerializer(many=True, read_only=True)
brand = BrandSerializer(many=False, read_only=True)
specifications = serializers.SerializerMethodField(source="get_specifications")
comments = ??????????????????????????????????????????????????
class Meta:
model = Item
fields = [
"id",
"slug",
"name",
"description",
"brand",
"show_price",
"location",
"specifications",
"is_visible",
"is_blocked",
"created_at",
"updated_at",
"seller",
"category",
"media",
"comments",
"users_wishlist",
"reported_by",
]
read_only = True
editable = False
lookup_field = "slug"
def get_specifications(self, obj):
return ItemSpecificationSerializer(obj.item_specification.all(), many=True).data
My views.py:
from rest_framework import viewsets, mixins, status
from ramrobazar.inventory.models import Item, Comment
from ramrobazar.drf.serializers ItemSerializer, ItemDetailSerializer, CommentSerializer
from rest_framework.permissions import IsAuthenticatedOrReadOnly
from rest_framework.filters import SearchFilter
from django_filters.rest_framework import DjangoFilterBackend
class ItemList(viewsets.GenericViewSet, mixins.ListModelMixin):
"""View for listing and retrieving all items for sale."""
queryset = Item.objects.all()
serializer_class = ItemSerializer
serializer_action_classes = {
"retrieve": ItemDetailSerializer,
}
permission_classes = [IsAuthenticatedOrReadOnly]
lookup_field = "slug"
filter_backends = [DjangoFilterBackend, SearchFilter]
filterset_fields = [
"category__slug",
"brand__name",
]
search_fields = ["name", "description", "category__name", "brand__name", "location"]
def get_serializer_class(self):
try:
return self.serializer_action_classes[self.action]
except:
return self.serializer_class
def retrieve(self, request, slug=None):
item = self.get_object()
serializer = self.get_serializer(item)
return Response(serializer.data)
#action(detail=True, methods=['GET'])
def comments(self, request, slug=None):
item = Item.objects.get(slug=slug)
queryset = Comment.objects.filter(item=item)
serializer = CommentSerializer(queryset, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
I have an action named comments in the ItemList view which gives all the comments of a specific item. I can get the details of the item from the url /api/items/<slug>. I can get all the comments of the item from the url api/items/<slug>/comments. I want to include a comments field in the ItemDetailSerializer serializer which is a link to api/items/<slug>/commments. How can I accomplish this?
You can do this via SerializerMethodField and reverse:
class ItemDetailSerializer(serializers.ModelSerializer):
...
comments = serializers.SerializerMethodField(source="get_comments")
...
def get_comments(self, obj):
return reverse(YOUR_URL_NAME, kwargs={'slug': obj.slug})
try this:
class ItemDetailSerializer(serializers.ModelSerializer):
...
comments = serializers.CharField(max_length=100, required=False)
def create(self, validated_data):
validated_data['comments'] = self.context['request'].build_absolute_uri() + 'comments'
return super(ContentGalleryListSerializer, self).create(validated_data)
You can use the HyperlinkedIdentityField for this:
comments = serializers.HyperlinkedIdentityField(
view_name='item-comments',
lookup_field='slug'
)
This will then render a comments field that contains a URL to the item-comments view, with the slug as lookup parameter.
You will need to register the view with the name item-comments, for example with:
urlpatterns = [
path('items/<slug>/comments/', ItemList.as_view({'get': 'comments'}), name='item-comments')
]
Note that normally nested routes are discouraged, and you'd put the comments in a separate view. But the above should work.
I have two models with many-to-many relationship and I also have nested routers between them.
When I'm trying to create a tag at the endpoint api/page/4/tags/, tag is created in the database for tags, but nothing happend in my table for many to many relationship. How can I fix it?
I want to update my M2M everytime a new tag is created. Thank you
models.py
class Tag(models.Model):
title = models.CharField(max_length=30, unique=True)
class Page(models.Model):
...
tags = models.ManyToManyField(Tag, related_name='pages')
My serializers.py
class TagSerializer(serializers.ModelSerializer):
class Meta:
model = Tag
fields = '__all__'
class PageSerializer(serializers.ModelSerializer):
tags = TagSerializer(many=True)
owner = serializers.HiddenField(default=serializers.CurrentUserDefault())
class Meta:
model = Page
fields = ['id', 'title', 'uuid', 'description', 'owner', 'is_private', 'tags']
views.py
class PageViewSet(viewsets.ModelViewSet):
queryset = Page.objects.all()
serializer_class = PageSerializer
def perform_update(self, serializer):
serializer.save(owner=self.request.user)
class TagViewSet(viewsets.ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
class NestedTagViewSet(CreateModelMixin, ListModelMixin, GenericViewSet):
queryset = Tag.objects.all()
serializer_class = TagSerializer
permission_classes = (IsAuthenticatedOrReadOnly,)
def get_page(self, request, page_pk=None):
page = get_object_or_404(Page.objects.all(), pk=page_pk)
self.check_object_permissions(self.request, page)
return page
def create(self, request, *args, **kwargs):
self.get_page(request, page_pk=kwargs['page_pk'])
return super().create(request, *args, **kwargs)
def get_queryset(self):
return Tag.objects.filter(pages=self.kwargs['page_pk'])
def list(self, request, *args, **kwargs):
self.get_page(request, page_pk=kwargs['page_pk'])
return super().list(request, *args, **kwargs)
my urls.py
from django.urls import path, include
from rest_framework_nested import routers
from .views import PageViewSet, TagViewSet, NestedTagViewSet
router = routers.SimpleRouter()
router.register(r'pages', PageViewSet)
pages_router = routers.NestedSimpleRouter(router, r'pages', lookup='page')
pages_router.register(r'tags', NestedTagViewSet, basename='page-tags')
app_name = 'page'
urlpatterns = [
path(r'', include(router.urls)),
path(r'', include(pages_router.urls)),
]
I'm trying to create new tags at the api/ ^pages/(?P<page_pk>[^/.]+)/tags/$ [name='page-tags-list'] host
I will assume after you create a tag you want to add it to the current page in the route.
With this in mind, you have a tag viewset, so you need to override the create method when you are creating a tag.
class TagViewSet(viewsets.ModelViewSet):
serializer_class = TagSerializer
queryset = Tag.objects.all()
def create(self, request, *args, **kwargs):
response = super().create(request, *args, **kwargs)
new_tag = Tag.objects.get(id=response["id"])
page = Page.objects.get(id=kwards["page_pk"])
page.tags.add(new_tag)
return response
The idea is to create a tag and after you create it, append it to the page M2M relationship. Hope this helps.
New to Django and DRF, I have a method in the model properties which accept arguments. I have managed to call it successful though a serializer class with default paramenters and getting a JSON response. My problem is I can't pass argument to that function named balance. I have successful pass my argument from view to serializer class but from serializer to model that where I have failed. I thought will be appreciated.
model.py
class Item(models.Model):
entered_by = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=50, blank=True)
#property
def balance(self, stock_type='Retail'):
stock = Stock.objects.filter(item=self, type=stock_type, status='InStock').aggregate(models.Sum('quantity')).get('quantity__sum')
return stock or 0
views.py
def getItemInfo(request):
if request.is_ajax and request.method == "GET":
id = request.GET.get("item_id", None)
sell_type = request.GET.get("sell_type", None)
item = Item.objects.get(id=id)
if item:
serializer = ItemSerializer(item, context={'sell_type':sell_type})
return JsonResponse(serializer.data, status = 200, safe=False)
else:
return JsonResponse({"data":False}, status = 400)
serializer.py
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
balance = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ('entered_by', 'name', 'balance', 'sell_mode')
def get_balance(self, object):
sell_type = self.context.get("sell_type")
if sell_type:
return object.balance(sell_type)
return object.balance
The error I'm getting
'int' object is not callable
#property couldn't be called. So I made member variable and setter (with calculation) methods in Item model, then make sure setter method will be called in get_balance serializer method, just before returning balance.
Django ORM model itself is just a class; you can do anything class could, not just only linking with ORM.
My Code:
models.py
from django.db import models
from django.contrib.auth.models import User
class Item(models.Model):
entered_by = models.ForeignKey(User, on_delete=models.PROTECT)
name = models.CharField(max_length=50, blank=True)
_balance = 0
def calculate_balance(self, stock_type='Retail'):
stock = Stock.objects.filter(item=self, type=stock_type, status='InStock').aggregate(
models.Sum('quantity')).get('quantity__sum')
self._balance = stock or 0
#property
def balance(self):
return self._balance
serializer.py
from django.contrib.auth.models import User
from rest_framework import serializers
from .models import Item
class ItemSerializer(serializers.ModelSerializer):
balance = serializers.SerializerMethodField()
class Meta:
model = Item
fields = ('entered_by', 'name', 'balance')
def get_balance(self, object):
sell_type = self.context.get("sell_type")
if sell_type:
object.calculate_balance(sell_type)
return object.balance
views.py
from .models import Item
from .serializer import ItemSerializer
from django.http.response import JsonResponse
def getItemInfo(request):
if request.is_ajax and request.method == "GET":
id = request.GET.get("item_id", None)
if id is None:
return JsonResponse({"data": False}, status=400)
sell_type = request.GET.get("sell_type", None)
try:
item = Item.objects.get(id=id)
serializer = ItemSerializer(item, context={'sell_type': sell_type})
return JsonResponse(serializer.data, status=200, safe=False)
except Item.DoesNotExist:
return JsonResponse({"data": False}, status=400)
Using Django REST framework I created an url which maps to a page with a JSON file containing all the objects in my database.
I want to do the same but instead of showing all the objects I want only the objects that match a specific category (category is an attribute in my model).
I have urls that show a JSON files with a single object in it (using the pk attribute) but when I try to do the same thing with category instead of pk I get a MultipleObjectsReturned error.
I'm just sperimenting with the REST framework, I tried using different views and class based views solving nothing.
Any hint or suggestion is really appreciated thanks.
# models.py
class Hardware(models.Model):
name = models.CharField(max_length=25)
category = models.CharField(choices=CATEGORY_CHOICES, max_length=2)
def get_api_url(self):
return api_reverse("category-api-postings:post-rud", kwargs={'category': self.category})
#views.py
class HardwareListView(generics.ListCreateAPIView):
pass
lookup_field = 'pk'
serializer_class = HardwareSerializer
def get_queryset(self):
query = self.request.GET.get("q")
qs = Hardware.objects.all()
if query is not None:
qs = qs.filter(Q(title__icontains=query) | Q(content__icontains=query)).distinct()
return qs
class HardwareRudView(generics.RetrieveUpdateDestroyAPIView):
pass
lookup_field = 'category'
serializer_class = HardwareSerializer
def get_queryset(self):
return Hardware.objects.all()
#urls.py
app_name = 'category-api-postings'
urlpatterns = [
path('', exercise_view),
path('list-api/', HardwareListView.as_view(), name='all'),
path('list-api/<str:category>/', HardwareRudView.as_view(), name='post-rud')
#serializer.py
class HardwareSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Hardware
fields = [
'url',
'pk',
'name',
'category'
]
read_only_fields = ['user']
def get_url(self, obj):
return obj.get_api_url()
As I understand, you want url /list-api/HD/ to return all Hardware objects from given category. For that HardwareRudView must inherit ListAPIView, not RetrieveUpdateDestroyAPIView. For example, like this:
class HardwareRudView(generics.ListAPIView):
serializer_class = HardwareSerializer
def get_queryset(self):
category = self.kwargs['category']
return Hardware.objects.filter(category=category)
See related docs: https://www.django-rest-framework.org/api-guide/filtering/#filtering-against-the-url
I have a realy simple API Endpoint which return a list of MyModel.
views.py:
#api_view(['GET'])
def index(request):
myModel = MyModel.objects.all()
serializer = MyModelSerializer(myModel, many=True)
return Response(serializer.data)
serializers.py
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ['field1', 'field2']
Now I'd like to return a serialized dictionary of models indexed by the primary key (which is a CharField).
{
'pk1':{
'field1': 'fo',
'field2': 'bar',
},
'pk2':{
'field1': 'fobar',
'field2': 'foo',
}
}
[EDIT] As requested, here is the model:
from django.db import models
class MyModel(models.Model):
id = models.CharField(primary_key=True)
field1 = models.FloatField(null=True)
field2 = models.FloatField(null=True)
You can override to_representation method in your serializer
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ['field1', 'field2']
def to_representation(self, instance):
ret = super().to_representation(instance)
return {instance.pk: ret}
You'll need to create a custom ListSerializer that will return a dictionary.
from django.db import models
from rest_framework import serializers
class DictSerializer(serializers.ListSerializer):
def to_representation(self, data):
iterable = data.all() if isinstance(data, models.Manager) else data
return [
(item.id, self.child.to_representation(item)) for item in iterable
]
#property
def data(self):
ret = super(DictSerializer, self).data
return serializers.ReturnDict(ret, serializer=self)
Then you'll have to specify that your ModelSerializer should use this custom when many=True. You do this by setting the list_serializer_class in the Meta of your ModelSerializer.
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ['field1', 'field2']
list_serializer_class = DictSerializer
If you want to be efficient following his Gabriel Muj solution is better but will not provide what you want out of the box. His solution will provide output like:
[{
'pk1': {
'field1': 'fo',
'field2': 'bar',
}
},
{
'pk2': {
'field1': 'fobar',
'field2': 'foo',
}
}
]
First change the serializer according to His solution:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = ['field1', 'field2']
def to_representation(self, instance):
ret = super().to_representation(instance)
return {instance.pk: ret}
But to get what you asked you need to use ChainMap from collections. Now you need to change your views like this:
from collections import ChainMap
#api_view(['GET'])
def index(request):
myModel = MyModel.objects.all()
serializer = MyModelSerializer(myModel, many=True)
data_dict = dict(ChainMap(*serializer.data))
return Response(data_dict)
For this snippet you need to make sure its python 3.3+ . This will provide good performance. While optimizing my code I have found this solution is less heavy.
You can iterate over the query set. In your index view:
#api_view(['GET'])
def index(request)
data = dict()
for element in MyModel.objects.all():
dict[element.id] = {'field1': element.field1, 'field2': element.field2}
return Response(data)