Django Rest Framework - Create object with foregin key relation - python

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")

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 i can realize multiple pagination(drf)

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.

How to create nested serialization from mutliple models uisng django rest framewrok

I am trying to create nested relationship from more than two models in Django Rest Framework.
Thank you in advance for helping me.
I succeed with two models but when I'm trying with three models unable to create nested serialization.
from django.db import models
class Project(models.Model):
project_id = models.AutoField(primary_key=True)
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_id = models.AutoField(primary_key=True)
site_name = models.CharField(max_length=255)
project_id= models.ForeignKey(Project, related_name="projectid", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
assign_id = models.AutoField(primary_key=True)
site_id = Models.ForeginKey(Site, relate_name="siteid", on_delete=models.CASCADE)
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
class Meta:
model = Project
fields = ('__all__')
class SiteSerializer(serializers.ModelSerializer):
class Meta:
model = Site
fields = ('__all__')
class AggignSerializer(serializers.ModelSerializer)
class Meta:
model = Aggin
fields = ('__all__')
I think you don't need to primary id field if you wanna use the Django's default foreign key setting. And related_name should be defined from the view of the counterpart model.
from django.db import models
class Project(models.Model):
project_name = models.CharField(max_length=255)
def __str__(self):
return self.name
class Site(models.Model):
site_name = models.CharField(max_length=255)
project = models.ForeignKey(Project, related_name="sites", on_delete=models.CASCADE)
def __str__(self):
return self.site_name
class Aggin(models.Model):
site = Models.ForeginKey(Site, relate_name="assigns", on_delete=models.CASCADE)
And then, in serializer, you can set like the following.
from rest_framework import serializers
from .models import Song, Artist
class ProjectSerializer(serializers.ModelSerializer):
sites = SiteSerializer(read_only = True, many = True)
class Meta:
model = Project
fields = ('id', 'project_name', 'sites')
class SiteSerializer(serializers.ModelSerializer):
assigns = AggignSerializer(read_only = True, many = True)
class Meta:
model = Site
fields = ('id', 'site_name', 'assigns')
class AggignSerializer(serializers.ModelSerializer):
class Meta:
model = Aggin
fields = ('id')

Name returning a number after serializing data

I have a table service :
from django.db import models
from users.models import CustomUser
SERVICE_CHOICES = (
('Carpenter', 'Carpenter'),
('Driver', 'Driver'),
('Ambulanve', 'Ambulanve'),
('Spa', 'Spa'),
('Barber', 'Barber'),
('Cleaning', 'Cleaning'),
('Cook', 'Cook'),
)
class Service(models.Model):
name = models.ForeignKey(CustomUser, on_delete=models.CASCADE, limit_choices_to={'is_worker': True},)
service = models.CharField(choices=SERVICE_CHOICES,max_length=100)
def __str__(self):
return f'{self.service} - {self.name}'
and a table CustomUser :
from django.db import models
from django.contrib.auth.models import AbstractUser
class CustomUser(AbstractUser):
is_worker = models.BooleanField(default=False)
is_customer = models.BooleanField(default=True)
I am serializing the Service table below :
from rest_framework import serializers
from django.contrib.auth.models import User
from .models import *
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
But when I add a service from the admin panel, the name shows a number in the browser and not the string. How do I change it to show a the name and not a number?
This is because name is a forignkey to CustomUser, and the default behavior is to return the PK related to the CustomUser instance.
Instead you could use SerializerMethodField.
class UserSerializer(serializers.ModelSerializer):
name = serializers.SerializerMethodField()
class Meta:
model = Service
fields = '__all__'
def get_name(self, obj):
return obj.name.first_name
In your ServiceSerializer you can try:
class ServiceSerializer(serializers.ModelSerializer):
class Meta:
model = Service
fields = '__all__'
def to_representation(self, instance):
rep = super(ServiceSerializer, self).to_representation(instance)
rep['name'] = instance.name.name # instance.name.what_you_use_in_Custom_User_model_to_represent_name
return rep
However, consider whether this is what you want, because this change could lead to more problems than it solves.

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