How i can realize multiple pagination(drf) - python

If I'll send get request like thisenter image description here, i need to have multiple pagination (LimitOffset and PageNumber).
models.py:
from django.db import models
class Products(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True)
photo = models.ImageField(upload_to="photos/%Y/%m/%d/", null=True)
hashtag = models.CharField(max_length=255)
is_hit = models.BooleanField(default=False)
category = models.ForeignKey('Category', on_delete=models.PROTECT, null=True)
def __str__(self):
return self.title
class Category(models.Model):
name = models.CharField(max_length=255)
def __str__(self):
return self.name
views.py:
from rest_framework import generics
from rest_framework.pagination import PageNumberPagination
from .models import *
from .serializers import ProductsSerializer
class PaginationProducts(PageNumberPagination):
page_size = 2
page_size_query_param = 'page_size'
max_page_size = 2
class ProductsAPIList(generics.ListCreateAPIView):
queryset = Products.objects.all()
serializer_class = ProductsSerializer
pagination_class = PaginationProducts
serializers.py
from rest_framework import serializers
from .models import *
class ProductsSerializer(serializers.ModelSerializer):
class Meta:
model = Products
fields = "__all__"
def get_photo_url(self, obj):
request = self.context.get('request')
photo_url = obj.fingerprint.url
return request.build_absolute_uri(photo_url)
I need something that can help API client choose number of page and quantity of posts on that page. Think that in this case i need NumberPagePagination and LimitOffsetPagination.

I think you don't need to create the custom pagination class.
from rest_framework.pagination import LimitOffsetPagination
class ProductsAPIList(generics.ListCreateAPIView):
queryset = Products.objects.all()
serializer_class = ProductsSerializer
pagination_class = LimitOffsetPagination
The offset value corresponds to the page * size in the original pagination and the limit value corresponds to the size.

Related

How to make the inventory decrease by 1 book when I borrow from the library, and increase by 1 when I return the book?

In the Book model, I made 2 methods to call them in the Borrowing field, but I haven't figured out how exactly to do it. And it is especially not clear how to connect the logic of returning the book. In the Borrowing model, there is only the actual_return_date field, when it is filled in, 1 should be added to the inventory, I think so. I tried to change the create method but it didn't work.
model Book:
from django.db import models
class Book(models.Model):
COVER_CHOICES = [("HARD", "Hard cover"), ("SOFT", "Soft cover")]
title = models.CharField(max_length=255)
authors = models.CharField(max_length=256)
cover = models.CharField(max_length=15, choices=COVER_CHOICES)
inventory = models.PositiveIntegerField()
daily_fee = models.DecimalField(max_digits=7, decimal_places=2)
class Meta:
ordering = ["title"]
def __str__(self):
return (
f"'{self.title}' by {self.authors}, "
f"cover: {self.cover}, "
f"daily fee: {self.daily_fee}, "
f"inventory: {self.inventory}"
)
def reduce_inventory_book(self):
self.inventory -= 1
self.save()
def increase_inventory_book(self):
self.inventory += 1
self.save()
book/view.py
from rest_framework import viewsets
from book.models import Book
from book.serializers import BookSerializer
class BookViewSet(viewsets.ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
book/serializers.py
from rest_framework import serializers
from book.models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = ("id", "title", "authors", "cover", "inventory", "daily_fee")
model Borrowing:
from django.conf import settings
from django.contrib.auth.models import AbstractUser
from django.db import models
from book.models import Book
class Borrowing(models.Model):
borrow_date = models.DateField(auto_now_add=True)
expected_return_date = models.DateField()
actual_return_date = models.DateField(null=True)
book = models.ForeignKey(Book, on_delete=models.PROTECT, related_name="borrowings")
user = models.ForeignKey(
settings.AUTH_USER_MODEL, on_delete=models.PROTECT, related_name="borrowings"
)
class Meta:
ordering = ["borrow_date"]
def __str__(self):
return f"{self.borrow_date}"
borrowing/views.py
from rest_framework import viewsets
from borrowing.models import Borrowing
from borrowing.serializers import BorrowingSerializer
class BorrowingViewSet(viewsets.ModelViewSet):
queryset = Borrowing.objects.all()
serializer_class = BorrowingSerializer
def get_queryset(self):
queryset = self.queryset
if not self.request.user.is_staff:
queryset = queryset.filter(user=self.request.user)
user_id = self.request.query_params.get("user_id")
is_active = self.request.query_params.get("is_active")
if str(self.request.user.id) == user_id and is_active:
queryset = queryset.filter(actual_return_date=None)
return queryset
def perform_create(self, serializer):
serializer.save(user=self.request.user)
serializer.save(book=self.request.book)
borrowing/serializers.py
from rest_framework import serializers
from book.models import Book
from book.serializers import BookSerializer
from borrowing.models import Borrowing
class BorrowingSerializer(serializers.ModelSerializer):
class Meta:
model = Borrowing
fields = (
"id",
"borrow_date",
"expected_return_date",
"actual_return_date",
"book",
)
def create(self, validated_data):
borrowing = Borrowing.objects.create(**validated_data)
borrowing.book.reduce_inventory_book()
return borrowing
I think that I need to change the credit method for this, but I don’t know how.
You can alter the save method of Borrowing so that when actual_return_date is filled to call the increase_inventory_book method

How do I add a post method for the api view so that posts can be commented on?

I'm building an API using DRF, and as I am a beginner, I can't get my head across this.
I'm building an instagram clone using DRF and was able to attach comments and likes to each post object, but I can only attach likes and comments using the admin panel. I can see the total number of likes/comments and the user who added them as JSON Output though. Is there a way to add an add comment_form to the post_detail view?
Here's my models.py file
from __future__ import unicode_literals
import uuid
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
# Create your models here.
class Createpost(models.Model):
id = models.UUIDField(
primary_key = True,
default=uuid.uuid4,
editable= False,
)
author = models.ForeignKey(User , on_delete=models.CASCADE)
title = models.CharField(max_length=50)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
post_image = models.ImageField(upload_to='images/',blank=True)
#property
def total_likes(self):
return Like.objects.filter(post_id = self.id).count()
#property
def likes(self):
array = []
for like in Like.objects.filter(post_id = self.id):
array.append(like.author.username)
return array
#property
def total_comments(self):
return Answers.objects.filter(post_id = self.id).count()
#property
def comments(self):
array = []
for comment in Answers.objects.filter(post_id = self.id):
c = {}
c['body'] = comment.body
c['username'] = comment.author.username
c['created_at'] = comment.created_at
array.append(c)
return array
def __str__(self):
return self.title
class Answers(models.Model):
post = models.OneToOneField(
Createpost,
primary_key=True,
on_delete = models.CASCADE,
)
body = models.TextField()
author = models.ForeignKey(User,on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
Updated_At = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=False)
def __str__(self):
return 'Comment {} by {} '.format(self.body,self.author)
class Like(models.Model):
post = models.OneToOneField(
Createpost,
primary_key=True,
on_delete = models.CASCADE,
)
author = models.ForeignKey(User,on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ('post','author',)
Here's my Views.py file:
from django.shortcuts import render
from rest_framework import generics
from .models import Createpost
from .permissions import IsAuthorOrReadOnly
from .serializers import PostSerializer,PostDetailSerializer,CommentSerializer
# Create your views here.
class PostList(generics.ListCreateAPIView):
queryset = Createpost.objects.all()
serializer_class = PostSerializer
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
permission_classes = (IsAuthorOrReadOnly,)
queryset = Createpost.objects.all()
serializer_class = PostDetailSerializer
Here's my serializers.py file:
from rest_framework import status
from rest_framework import serializers
from rest_framework.decorators import APIView
from .models import Createpost,Answers
from django.contrib.auth.models import User
class PostSerializer(serializers.ModelSerializer):
totallikes = serializers.ReadOnlyField(source = 'total_likes')
totalcomments = serializers.ReadOnlyField(source = 'total_comments')
class Meta:
fields = ('id','author','title','body','created_at','totallikes','totalcomments')
model = Createpost
class CommentSerializer(serializers.Serializer):
field1 = serializers.CharField()
class PostDetailSerializer(serializers.ModelSerializer):
li_kes = serializers.ReadOnlyField(source = 'likes')
com_ments = serializers.ReadOnlyField(source = 'comments')
class Meta:
fields = ('id','author','title','body','created_at','updated_at','post_image','li_kes','com_ments',)
model = Createpost
I don't see a comment model in your code, so it's hard to know exactly how you're going about this. But one pattern for adding comments to a post object would to create an endpoint that accepts the comment details in the request and saves them to the post. Something like:
from rest_framework import viewsets, status
class PostComment(viewsets.ModelViewSet):
""" API endpoint for adding comments to posts """
def create(self, request):
Data = request.data
payload = {'post': Data['post_id'], 'user': self.request.user.id, 'comment': Data['comment']]}
post = UserPost.objects.get(uniqueID=Data['post_id'])
payload['post'] = post.id
serializer = UserCommentReplyPOSTSerializer(data=payload)
if serializer.is_valid():
serializer.save()
return Response('Comment saved', status=status.HTTP_200_OK)
else:
print(serializer.errors)
return Response('There was a problem with your request', status=status.HTTP_400_BAD_REQUEST)
You would then register this endpoint in your urls.py and use it in your front-end comment POST routine.
Obviously you'd need a Comment model with a foreign key to to your Post model for this to work. I'm assuming you have that and didn't show it.

Django Rest Framework - Create object with foregin key relation

I created simple REST API which i want to create product objects. My problem is that API view is not showing me multiple choice field to choose from existing categories.
models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
class Product(models.Model):
name = models.CharField(max_length=200, unique=True)
price = models.PositiveSmallIntegerField(default="")
category = models.ForeignKey(Category, on_delete=models.CASCADE, blank=True, default=None)
def __str__(self):
return self.name
class Meta:
ordering = ('name',)
serializers.py
from rest_framework import serializers
from .models import Product, Category
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = '__all__'
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = '__all__'
views.py
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from .models import Product, Category
from .serilaizers import ProductSerializer, CategorySerializer
from rest_framework.response import Response
from rest_framework.views import APIView
class ProductView(viewsets.ModelViewSet):
queryset = Product.objects.all()
serializer_class = ProductSerializer
class CategoryView(viewsets.ModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
That is what i get after running server, only two positions:
__all__ does not work with ForeignKey and other relationship fields. You need to specify the field names explicitly. Add a list or tuple with field names to your ProductSerializer.
class ProductSerializer(serializers.ModelSerializer):
class Meta:
model = Product
fields = ("name", "price", "category")

Django dynamic URL with data from DB

Models.py
from django.db import models
# Create your models here.
class reviewData(models.Model):
building_name = models.CharField(max_length=50)
review_content = models.TextField()
star_num = models.FloatField()
class buildingData(models.Model):
building_name = models.CharField(max_length=50)
building_loc = models.CharField(max_length=50)
building_call = models.CharField(max_length=20)
views.py
# Create your views here.
from django.shortcuts import render
from rest_framework.response import Response
from .models import reviewData
from .models import buildingData
from rest_framework.views import APIView
from .serializers import ReviewSerializer
class BuildingInfoAPI(APIView):
def get(request):
queryset = buildingData.objects.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
class ReviewListAPI(APIView):
def get(request):
queryset = reviewData.objects.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
urls.py
from django.contrib import admin
from django.urls import path
from crawling_data.views import ReviewListAPI
from crawling_data.views import BuildingInfoAPI
urlpatterns = [
path('admin/', admin.site.urls),
path('api/buildingdata/', BuildingInfoAPI.as_view()),
#path('api/buildingdata/(I want to put building name here)', ReviewListAPI.as_view())
]
I am making review api.
I want to use building name as url path to bring reviews for specific buildings
For example, there are a, b, c reviews
a, b reviews are for aaabuilding
c reviews are for xxxbuilding
api/buildingdata/aaabuilding (only shows aaabuilding review)
{
building_name = aaabuilding
review_content = a
star_num = 5
building_name = aaabuilding
review_content = b
star_num = 3
}
api/buildingdata/xxxbuilding (only shows xxxbuilding review)
{
building_name = xxxbuilding
review_content = c
star_num = 4
}
I've searched some dynamic URL posts, but they were not that I want.
Also, I've posted a question before but there was no answer I was looking for.
Is there any way to bring building name into URL from db?
Please refer to the documentation on path converters. And usage of django's slugify function.
In your situation you will want a slug - but there are limitations to using a slug:
slugs must translate to some unique string, in your case building name. Therefore you should make sure that building name and slug are unique in your model.
You should add a slug field to the model - and also change the review model so it foreign keys to the building model:
from django.utils.text import slugify
class buildingData(models.Model):
building_name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True)
building_loc = models.CharField(max_length=50)
building_call = models.CharField(max_length=20)
def save(self, *args, **kwargs):
self.slug = slugify(self.building_name)
return super().save(*args, **kwargs)
class reviewData(models.Model):
building = models.ForeignKey(buildingData, related_name='reviews', on_delete=models.CASCADE, null=False, blank=False)
review_content = models.TextField()
star_num = models.FloatField()
Then in your urls.py:
path('api/buildingdata/<slug:slug>/', ReviewListAPI.as_view())
Then in your Views.py:
class ReviewListAPI(APIView):
def get(self, request):
building = get_object_or_404(buildingData, slug=self.kwargs.get('slug')
queryset = building.reviews.all()
serializer = ReviewSerializer(queryset, many=True)
return Response(serializer.data)
Also review pep8 - your class names should really be BuildingData and ReviewData - and you problably dont need Data in the names either.

How to create views.py for multiple models including many-to-many

I'm new to Python and DjangoRestFramework. I am trying to create an image upload system with image-tagging. "Tags" have a many-to-many relationship with "Images". The forms are in the React.js in the front-end. I am trying to understand how to write a view for this. I have not seen a clear solution to this online.
here is upload/models.py
from django.db import models
from django.db.models.fields import UUIDField
from django.contrib.postgres.functions import RandomUUID
def upload_path(instance, filename):
return '/'.join(['images', str(instance.contributor), str(instance.caption), str(instance.date_created), filename])
class Image(models.Model):
image = models.ImageField(upload_to=upload_path)
contributor = models.ForeignKey(
'accounts.User', related_name='+', on_delete=models.CASCADE, default='user0')
caption = models.CharField(max_length=100)
date_created = models.DateTimeField(auto_now_add=True, null=True)
id = UUIDField(primary_key=True, default=RandomUUID, editable=False)
theme = models.CharField(max_length=10)
class Tags(models.Model):
tag = models.ManyToManyField(Image, through='Junction')
class Junction(models.Model):
image = models.ForeignKey(Image, on_delete=models.CASCADE)
tags = models.ForeignKey(Tags, on_delete=models.CASCADE)
upload/serializers.py
from rest_framework import serializers
from .models import Image, Theme, Tags, Junction
class TagsSerializer(serializers.ModelSerializer):
tags = serializers.PrimaryRelatedKeyField(
queryset=Image.object.all(), many=True)
class Meta:
model = Tags
fields = ('Tags')
class ImageSerializer(serializers.ModelSerializer):
tags_list = TagsSerializer(many=True, read_only=True)
class Meta:
model = Image
fields = ('image', 'contributor', 'caption', 'date_created', 'id')
class JunctionSerializer(serializers.ModelSerializer):
class Meta:
model = Junction
fields = ('image', 'theme', 'tags')
Here are two possible solutions for the upload/views.py:
from django.shortcuts import render
from django.http import HttpResponse
from rest_framework import viewsets
from .serializers import JunctionSerializer
from .models import Image, Tags, Junction
#SOLUTION_1:
class JunctionView(viewsets.ModelViewSet):
serializer_class = JunctionSerializer
query_set = Junction.objects.all()
def get_context_data(self, request, *args, **kwargs):
image = request.data['cover']
tags = request.data['tags']
Junction.objects.create(image=image, tags=tags)
return HttpResponse({'message': 'Successful Upload'}, status=200)
#SOLUTION_2
class JunctionView():
????
def get_context_data(self, **kwargs):
context = super(JunctionView, self).get_context_data(**kwargs)
context['image'] = Image.objects.all()
context['tags'] = Tags.objects.all()
return context
Is it necessary to call the context? The second solution that I researched is not specific to Django REST Framework, I think..
One approach is to create a ModelViewSet for the images, since you are working mainly with managing the images.
class ImageViewSet(viewsets.ModelViewSet):
serializer_class = ImageSerializer
query_set = Image.objects.all()
And then using your serializer, just modify your serializer to handle the m2m relations with a through model as described here. So something like:
class ImageSerializer(serializers.ModelSerializer):
tags_list = TagsSerializer(many=True, read_only=True)
class Meta:
model = Image
fields = ('image', 'contributor', 'caption', 'date_created', 'id')
def create(self, validated_data):
tags_list = validated_data.pop('tags_list')
instance = super().create(validated_data)
for tag in tags_list:
Junction.objects.create(image=instance, tag=tag)
This is not tested so it might have some problems here and there but this is the gist.

Categories