How can I update specific field after retrieved in django rest framework - django

How can I update specific field after retrieved in django rest framework
# Models.py
class Article(models.Model):
title = models.CharField(max_length=255)
body = models.TextField()
view = models.IntegerField(default=0)
def __str__(self):
return self.title
I want to update view after read a specific data.
# Views.py
class ArticleDetail(generics.RetrieveUpdateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# Update view + 1
# serializers.py
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = "__all__"
Please help me

If you want your field to be incremented only on a GET request, you can update it in the retrieve method:
# views.py
class ArticleDetail(generics.RetrieveUpdateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
instance.view = instance.view + 1
instance.save()
serializer = self.get_serializer(instance)
return Response(serializer.data)
If you want it to be incremented with both GET and PATCH, you could update it in get_object instead:
# views.py
class ArticleDetail(generics.RetrieveUpdateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def get_object(self):
instance = super(ArticleDetail, self).get_object()
instance.view = instance.view + 1
instance.save()
return instance

Related

Why Django's OneToOneField returns 500 if relation aready exists

In a Django REST Framework POST view, is there any way to avoid an HTTP 500 if the OneToOneField relation already exists?
Instead, it would be great to get an HTTP 400.
models.py
class Club(TimeStampModel):
owner = models.OneToOneField(User, on_delete=models.PROTECT)
name = models.CharField(max_length=255, unique=True)
serializers.py
class ClubSerializer(serializers.ModelSerializer):
class Meta:
model = Club
fields = '__all__'
read_only_fields = ['owner', 'active']
views.py
class ClubRegistrationView(generics.CreateAPIView):
queryset = Club.objects.all()
serializer_class = ClubSerializer
permission_classes = [IsAuthenticated]
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
Thanks.
When you passed owner like this serializer.save(owner=self.request.user) serializer doesn't perform validation. You should rewrite create instead of perform_create method for this
class ClubRegistrationView(generics.CreateAPIView):
queryset = Club.objects.all()
serializer_class = ClubSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
request.data["owner"] = request.user.pk
return super().create(request, *args, **kwargs)
Here a validation error:
{'user': ['This field must be unique.']}

self.request.user does not work in Django REST framework

I want to give only the information of the user who requested with self.request.user, but for some reason, this way I get the information of all users.
How can I improve this so that we can retrieve only the information of the user who requested it?
Please let me know if you know anything about this.
View
class StatisticsViewSet(APIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = EntrySerializer
def get(self, request):
queryset = self.request.user.entries.all()
serializer = StatisticsSerializer(queryset)
return Response(serializer.data)
Serializer
class StatisticsSerializer(serializers.Serializer):
daily_report = serializers.SerializerMethodField()
daily_report_week = serializers.SerializerMethodField()
def get_daily_report(self, obj):
data = Entry.objects.filter(user=obj.user).values(
'created_at__year', 'created_at__month',
'created_at__day').annotate(Sum('time')).order_by(
'created_at__year', 'created_at__month', 'created_at__day')
........
...
...
Model
class Entry(models.Model):
project = models.ForeignKey(Project,
related_name='entries',
on_delete=models.CASCADE)
time = models.IntegerField(default=0)
user = models.ForeignKey(User,
related_name='entries',
on_delete=models.CASCADE)
created_at = models.DateTimeField(default=datetime.now)
def __str__(self):
return '%s - %s' % (self.project.name, self.created_at)
You can filter the queryset inside the serializer using the context
Serializer:
class StatisticsSerializer(serializers.Serializer):
daily_report = serializers.SerializerMethodField()
daily_report_week = serializers.SerializerMethodField()
def get_daily_report(self, obj):
user = self.context['request'].user
data = Entry.objects.filter(user=user).values(
'created_at__year', 'created_at__month',
'created_at__day').annotate(Sum('time')).order_by(
'created_at__year', 'created_at__month', 'created_at__day')
View:
class StatisticsViewSet(APIView):
permission_classes = [permissions.IsAuthenticated]
serializer_class = EntrySerializer
def get(self, request):
queryset = self.request.user.entries.all()
serializer = StatisticsSerializer(queryset, context = {'request': self.request)
return Response(serializer.data)

Django with Postgresql update object, prevent order re-ordering

in django production mode with postgreSQL, When I got an still existing object/row from the database, edit/update it and store it back, the row is moved to the end of the table.
object 1
object 2
object 3
after done editing object 1
object 2
object 3
object 1
models.py
class itemModels(models.Model):
name = models.CharField(max_length=50)
photo = models.ImageField(max_length=200, blank=True, default="default.png")
photo2 = models.ImageField(max_length=200, blank=True, default="default2.png")
price = models.DecimalField(max_digits=6, decimal_places=2)
sku = models.CharField(max_length=50, unique=True)
description = models.CharField(max_length=255)
def __self__(self):
return self.name
views.py
class itemViewSet(viewsets.ModelViewSet):
queryset = itemModels.objects.all()
serializer = itemSerializers()
renderer_classes = [TemplateHTMLRenderer]
template_name = 'product-list.html'
lookup_field = 'pk'
# list all item
def list(self, request):
queryset = itemModels.objects.all()
context = {'posts': queryset}
return Response(context)
# serializer form
def serializer_list(self, request):
serializer = itemSerializers()
return Response({'serializer': serializer}, template_name='product-create.html')
def retrieve(self, request, pk):
profile = get_object_or_404(itemModels, pk=pk)
serializer = itemSerializers(profile)
return Response({'serializer': serializer}, template_name='product-edit.html')
def update(self, request, pk):
profile = get_object_or_404(itemModels, pk=pk)
serializer = itemSerializers(profile, data=request.data)
if serializer.is_valid():
serializer.save()
print('status: status.Item Update')
return redirect('item')
else:
print('status: status.Item Bad Update')
i want to prevent this re-ordering happen, is there something i miss out ? thanks guys
Note:
if i am using development mode and switch the to django dummie database, this wont happen.
def list(self, request):
queryset = itemModels.objects.all()
context = {'posts': queryset}
return Response(context)
to
def list(self, request):
queryset = itemModels.objects.order_by('id').all()
context = {'posts': queryset}
return Response(context)
credit for #snakecharmerb thanks

DRF: django rest framework case insensitive lookup in detail view

How to make urls case insensitive with certain parameters passed
For example, assuming Stock model has a ticker. All links below should find the same ticker content, right now they are case sensitive and try to search for different values:
/stocks/AAPL
/stocks/aapl
/stocks/AaPl
views.py
class StockViewSet(viewsets.ModelViewSet):
queryset = Stock.objects.all()
serializer_class = StockSerializer
lookup_field = "ticker"
#action(detail=True, methods=["get"], url_path="is", url_name="is")
def get_income_statement(self, request, *args, **kwargs):
is_qs = IncomeStatement.objects.filter(ticker=self.get_object())
serializer = IncomeStatementSerializer(is_qs, many=True)
return Response(serializer.data)
#action(detail=True, methods=["get"], url_path="bs", url_name="bs")
def get_balance_sheet(self, requests, *args, **kwargs):
bs_qs = BalanceSheet.objects.filter(ticker=self.get_object())
serializer = BalanceSheetSerializer(bs_qs, many=True)
return Response(serializer.data)
#action(detail=True, methods=["get"], url_path="cf", url_name="cf")
def get_cashflows_statement(self, requests, *args, **kwargs):
cf_qs = CashflowsStatement.objects.filter(self.get_object())
serializer = CashflowsStatementSerializer(cf_qs, many=True)
return Response(serializer.data)
class IncomeStatementDetail(viewsets.ModelViewSet):
queryset = IncomeStatement.objects.all()
serializer_field = IncomeStatementSerializer
class BalanceSheetDetail(viewsets.ModelViewSet):
queryset = BalanceSheet.objects.all()
serializer_field = BalanceSheetSerializer
class CashflowsStatementDetail(viewsets.ModelViewSet):
queryset = CashflowsStatement.objects.all()
serializer_field = CashflowsStatementSerializer
urls.py
router = DefaultRouter()
router.register(r"stocks", views.StockViewSet)
urlpatterns = router.urls
models.py
class Stock(models.Model):
id = models.UUIDField(default=uuid.uuid4, editable=False, unique=True)
ticker = models.CharField(max_length=10, unique=True, primary_key=True)
slug = models.SlugField(default="", editable=False)
def save(self, *args, **kwargs):
value = self.ticker
self.slug = slugify(value, allow_unicode=True)
super().save(*args, **kwargs)
def __str__(self):
return self.ticker
class Meta:
verbose_name = "stock"
verbose_name_plural = "stocks"
ordering = ["ticker"]
Use lookup_url_kwarg and lookup_field as
from rest_framework import viewsets
class StockViewSet(viewsets.ModelViewSet):
lookup_url_kwarg = 'ticker'
lookup_field = 'ticker__iexact'
# rest of your code
You can refer the source code of get_object(self) to see how DRF fetching the model object in the detail view.

Django getting ForeignKey objects in APIView

Hey so I am new to Django and I am writing a REST API with the following model .
# models.py
class Order(models.Model):
order_name = models.CharField(max_length=10, unique=True, default="")
def __str__(self):
return '{0}'.format(self.order_name).
class LineItem(models.Model):
order = models.ForeignKey(Order, on_delete=models.SET_NULL, null=True)
product = models.ForeignKey(Product, on_delete=models.SET_NULL, null=True)
class Meta:
unique_together = ('order', 'product')
def __str__(self):
return '{} - {}'.format(self.order.order_name, self.product.product_name).
# views.py
class OrderList(APIView):
def get(self, request):
orderlist = Order.objects.all()
serializer = OrderSerializer(orderlist, many=True)
return Response(serializer.data)
def post(self, request):
serializer = OrderSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class OrderDetail(APIView):
def get_object(self, order):
try:
return Order.objects.get(order_name=order_name)
except Order.DoesNotExist:
raise Http404
def get(self, request, order_name):
snippet = Order.objects.get(order_name=order_name)
snippet = snippet.lineitem_set.all()
serializer = OrderSerializer(snippet)
return Response(serializer.data)
So I am trying to code the OrderDetail(APIView)'s get method so that at /api/order/OrderA/ I get JSON with all the lineitems in that order. I've been struggling for a while with this now. Any suggestions?
class OrderSerializer(serializers.ModelSerializer):
class Meta:
model = Order
fields = ['order_name']
Change your serializer as below,
class LineItemSerializer(serializers.ModelSerializer):
class Meta:
model = LineItem
fields = ('id', 'product')
depth = 1
class OrderSerializer(serializers.ModelSerializer):
line_items = LineItemSerializer(many=True, source='lineitem_set')
class Meta:
model = Order
fields = ['order_name', 'line_items']
and then in your OrderDetail view as
class OrderDetail(APIView):
# your code
def get(self, request, order_name):
snippet = Order.objects.get(order_name=order_name)
serializer = OrderSerializer(snippet)
return Response(serializer.data)
DRF gives you a class to get the details of an object. Have a look here: http://www.django-rest-framework.org/api-guide/generic-views/#retrieveapiview
This an example code that should work if the order_name is passed as param in the url using a GET method.
from django.views.decorators.cache import never_cache
from django.utils.decorators import method_decorator
from rest_framework.exceptions import NotFound
from rest_framework.generics import RetrieveAPIView
# the decorator avoid to cache the object, maybe you don't need that.
#method_decorator(never_cache, name="dispatch")
class OrderDetail(RetrieveAPIView):
serializer_class = OrderSerializer
def get_object(self):
try:
return Order.objects.get(order_name=self.kwargs.get('order_name'))
except Order.DoesNotExist:
raise NotFound()