Changed work of shelf books

This commit is contained in:
maciejrusek
2026-03-22 14:19:17 +01:00
parent 5722f0b7ea
commit aeab43aa8a
23 changed files with 307 additions and 140 deletions

View File

View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

View File

@@ -0,0 +1,5 @@
from django.apps import AppConfig
class ShelfBooksConfig(AppConfig):
name = 'apps.shelf_books'

View File

@@ -0,0 +1,29 @@
# Generated by Django 6.0.3 on 2026-03-22 12:16
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('books', '0001_initial'),
('shelves', '0002_initial'),
]
operations = [
migrations.CreateModel(
name='ShelfItem',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('quantity', models.PositiveIntegerField(default=0)),
('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='shelf_items', to='books.book')),
('shelf', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='book_items', to='shelves.shelf')),
],
options={
'constraints': [models.UniqueConstraint(fields=('book', 'shelf'), name='unique_book_per_shelf')],
},
),
]

View File

@@ -0,0 +1,19 @@
# Generated by Django 6.0.3 on 2026-03-22 12:53
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('books', '0001_initial'),
('shelf_books', '0001_initial'),
('shelves', '0002_initial'),
]
operations = [
migrations.RenameModel(
old_name='ShelfItem',
new_name='ShelfBook',
),
]

View File

@@ -0,0 +1,19 @@
from django.db.models import Q, F
from django.db import models
from apps.books.models import Book
from apps.shelves.models import Shelf
class ShelfBook(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="shelf_items")
shelf = models.ForeignKey(Shelf, on_delete=models.CASCADE, related_name="book_items")
quantity = models.PositiveIntegerField(default=0)
class Meta:
constraints = [
models.UniqueConstraint(
fields=["book", "shelf"],
name="unique_book_per_shelf"
),
]

View File

@@ -0,0 +1,84 @@
from rest_framework import serializers
from apps.shelf_books.models import ShelfBook
from apps.books.models import Book
from apps.shelves.models import Shelf
from apps.shelves.serializers import ShelfSerializer
from apps.books.serializers import BookSerializer
class ShelfBookSerializer(serializers.ModelSerializer):
shelf = ShelfSerializer(read_only=True)
book = BookSerializer(read_only=True)
book_id = serializers.PrimaryKeyRelatedField(
queryset=Book.objects.all(),
source="book",
write_only=True
)
class Meta:
model = ShelfBook
fields = [
"id",
"shelf",
"book",
"book_id",
"quantity"
]
read_only_fields = ["id"]
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
request = self.context.get("request")
if request and request.user and request.user.is_authenticated:
self.fields["book_id"].queryset = Book.objects.filter(
user=request.user
)
else:
self.fields["book_id"].queryset = Book.objects.none()
def validate(self, attrs):
attrs = super().validate(attrs)
book = attrs.get("book")
shelf = self.context.get("shelf")
if not book:
book = self.instance.book
if not shelf:
return attrs
qs = ShelfBook.objects.filter(
book=book,
shelf=shelf
)
if self.instance:
qs = qs.exclude(pk=self.instance.pk)
if qs.exists():
raise serializers.ValidationError(
"This Book already exists in shelf!"
)
if book.user_id != shelf.user_id:
raise serializers.ValidationError(
"Book and Shelf must be from the same user!"
)
return attrs
def create(self, validated_data):
shelf = self.context["shelf"]
validated_data["shelf"] = shelf
return super().create(validated_data)
def update(self, instance, validated_data):
shelf = self.context["shelf"]
validated_data["shelf"] = shelf
return super().update(instance, validated_data)

View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

View File

@@ -0,0 +1,10 @@
from django.urls import path
from apps.shelf_books.views import ShelfBooks, SelectedShelfBook
urlpatterns = [
path("shelves/<int:shelf_id>/books/", ShelfBooks.as_view()),
path("shelves/<int:shelf_id>/books/<int:book_id>/", SelectedShelfBook.as_view())
]

View File

@@ -0,0 +1,84 @@
from rest_framework import generics
from apps.shelf_books.models import ShelfBook
from apps.shelves.models import Shelf
from apps.shelf_books.serializers import ShelfBookSerializer
from rest_framework.permissions import IsAuthenticated
from rest_framework_simplejwt.authentication import JWTAuthentication
from drf_spectacular.utils import extend_schema, extend_schema_view
@extend_schema_view(
get=extend_schema(tags=["Shelf Books"]),
post=extend_schema(tags=["Shelf Books"]),
)
class ShelfBooks(generics.ListCreateAPIView):
queryset = ShelfBook.objects.all()
serializer_class = ShelfBookSerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
def get_queryset(self):
shelf_id = self.kwargs["shelf_id"]
return ShelfBook.objects.filter(
shelf__id=shelf_id,
shelf__user_id=self.request.user.pk,
).select_related("book", "shelf").prefetch_related("book__authors")
def get_serializer_context(self):
context = super().get_serializer_context()
shelf_id = self.kwargs["shelf_id"]
shelf = Shelf.objects.filter(
id=shelf_id,
user=self.request.user
).first()
context["shelf"] = shelf
return context
@extend_schema_view(
get=extend_schema(tags=["Shelf Books"]),
put=extend_schema(tags=["Shelf Books"]),
patch=extend_schema(tags=["Shelf Books"]),
delete=extend_schema(tags=["Shelf Books"]),
)
class SelectedShelfBook(generics.RetrieveUpdateDestroyAPIView):
queryset = ShelfBook.objects.all()
serializer_class = ShelfBookSerializer
authentication_classes = [JWTAuthentication]
permission_classes = [IsAuthenticated]
lookup_url_kwarg = "book_id"
def get_queryset(self):
return ShelfBook.objects.filter(
shelf_id=self.kwargs["shelf_id"],
shelf__user=self.request.user,
).select_related("book", "shelf").prefetch_related("book__authors")
def get_object(self):
queryset = self.get_queryset()
return generics.get_object_or_404(
queryset,
book_id=self.kwargs["book_id"]
)
def get_serializer_context(self):
context = super().get_serializer_context()
shelf = Shelf.objects.filter(
id=self.kwargs["shelf_id"],
user=self.request.user
).first()
context["shelf"] = shelf
return context