Skip to content

Commit 3c58152

Browse files
committed
fix filters
1 parent 7f88a63 commit 3c58152

15 files changed

Lines changed: 96 additions & 630 deletions

File tree

Makefile

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,5 +134,11 @@ health: ## Check services health
134134
@echo "\n--- Service Health ---"
135135
@curl -f http://localhost:8000/health/ || echo "Django service not responding"
136136

137-
parse:
138-
$(PYTHON) manage.py parse_books
137+
books:
138+
$(PYTHON) manage.py parse_books
139+
140+
tags:
141+
$(PYTHON) manage.py create_default_tags
142+
143+
assign:
144+
$(PYTHON) manage.py assign_tags_to_books

apps/books/api/v1/filters.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@ class BookFilter(FilterSet):
77
title = CharFilter(
88
lookup_expr="icontains",
99
)
10-
author = CharFilter(
11-
field_name="author__last_name",
12-
lookup_expr="icontains",
10+
author = NumberFilter(
11+
field_name="author__id",
12+
lookup_expr="exact",
1313
)
1414
publisher = NumberFilter(
1515
field_name="publisher__id",

apps/books/api/v1/views.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ def get_serializer_class(self):
6161
return BookSerializer
6262

6363

64+
6465
class CommentViewSet(viewsets.ModelViewSet):
6566
queryset = Comment.objects.select_related("user", "book")
6667
serializer_class = CommentSerializer

apps/books/urls.py

Lines changed: 0 additions & 13 deletions
This file was deleted.

apps/books/views.py

Lines changed: 3 additions & 128 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,3 @@
1-
from django.contrib.auth.decorators import login_required
2-
from django.core.paginator import Paginator
3-
from django.db.models import Q
4-
from django.http import HttpResponse
5-
from django.shortcuts import get_object_or_404, render
6-
from django.views.decorators.csrf import csrf_exempt
7-
8-
from .models import Book, Comment, Publisher
9-
10-
11-
def index(request):
12-
books = (
13-
Book.objects.select_related("publisher")
14-
.prefetch_related("author", "tags")
15-
.order_by("-created")
16-
)
17-
publishers = Publisher.objects.all()
18-
19-
# Apply filters
20-
search = request.GET.get("search", "")
21-
category = request.GET.get("category", "")
22-
publisher_id = request.GET.get("publisher", "")
23-
sort_by = request.GET.get("sort", "-created")
24-
25-
if search:
26-
books = books.filter(
27-
Q(title__icontains=search)
28-
| Q(description__icontains=search)
29-
| Q(author__first_name__icontains=search)
30-
| Q(author__last_name__icontains=search)
31-
).distinct()
32-
33-
if category:
34-
books = books.filter(tags__slug=category)
35-
36-
if publisher_id:
37-
books = books.filter(publisher_id=publisher_id)
38-
39-
books = books.order_by(sort_by)
40-
41-
# Pagination
42-
paginator = Paginator(books, 12)
43-
page_number = request.GET.get("page")
44-
page_obj = paginator.get_page(page_number)
45-
46-
# Check if this is an HTMX request
47-
if request.headers.get("HX-Request"):
48-
return render(request, "books/partials/books_grid.html", {"books": page_obj})
49-
50-
return render(
51-
request,
52-
"books/index.html",
53-
{
54-
"books": page_obj,
55-
"publishers": publishers,
56-
},
57-
)
58-
59-
60-
def book_detail(request, book_id):
61-
book = get_object_or_404(Book, id=book_id)
62-
63-
# Check if this is an HTMX request for modal
64-
if request.headers.get("HX-Request"):
65-
return render(request, "books/detail.html", {"book": book})
66-
67-
return render(request, "books/detail.html", {"book": book})
68-
69-
70-
def book_search(request):
71-
search = request.GET.get("search", "")
72-
books = Book.objects.select_related("publisher").prefetch_related("author", "tags")
73-
74-
if search:
75-
books = books.filter(
76-
Q(title__icontains=search)
77-
| Q(description__icontains=search)
78-
| Q(author__first_name__icontains=search)
79-
| Q(author__last_name__icontains=search)
80-
).distinct()
81-
82-
books = books.order_by("-created")
83-
84-
# Pagination
85-
paginator = Paginator(books, 12)
86-
page_number = request.GET.get("page")
87-
page_obj = paginator.get_page(page_number)
88-
89-
return render(request, "books/partials/books_grid.html", {"books": page_obj})
90-
91-
92-
def book_filter(request):
93-
books = Book.objects.select_related("publisher").prefetch_related("author", "tags")
94-
95-
# Apply filters
96-
category = request.GET.get("category", "")
97-
publisher_id = request.GET.get("publisher", "")
98-
sort_by = request.GET.get("sort", "-created")
99-
100-
if category:
101-
books = books.filter(tags__slug=category)
102-
103-
if publisher_id:
104-
books = books.filter(publisher_id=publisher_id)
105-
106-
books = books.order_by(sort_by)
107-
108-
# Pagination
109-
paginator = Paginator(books, 12)
110-
page_number = request.GET.get("page")
111-
page_obj = paginator.get_page(page_number)
112-
113-
return render(request, "books/partials/books_grid.html", {"books": page_obj})
114-
115-
116-
@login_required
117-
@csrf_exempt
118-
def add_comment(request, book_id):
119-
if request.method == "POST":
120-
book = get_object_or_404(Book, id=book_id)
121-
text = request.POST.get("text", "").strip()
122-
123-
if text:
124-
comment = Comment.objects.create(book=book, user=request.user, text=text)
125-
126-
return render(request, "books/partials/comment.html", {"comment": comment})
127-
128-
return HttpResponse("", status=400)
1+
# Файл больше не содержит view-функций для рендеринга HTML шаблонов.
2+
# Весь фронтенд теперь находится в папке frontend/.
3+
# API views находятся в apps/books/api/v1/views.py

config/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
TEMPLATES = [
6363
{
6464
"BACKEND": "django.template.backends.django.DjangoTemplates",
65-
"DIRS": [os.path.join(BASE_DIR, "templates")],
65+
"DIRS": [],
6666
"APP_DIRS": True,
6767
"OPTIONS": {
6868
"context_processors": [

docker-compose.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ services:
1313
volumes:
1414
- ./apps:/app/apps
1515
- ./config:/app/config
16-
- ./templates:/app/templates
1716
- ./static:/app/static
1817
- ./manage.py:/app/manage.py
1918
depends_on:
@@ -77,7 +76,6 @@ services:
7776
volumes:
7877
- ./apps:/app/apps
7978
- ./config:/app/config
80-
- ./templates:/app/templates
8179
- ./static:/app/static
8280
- ./manage.py:/app/manage.py
8381
env_file:
@@ -104,7 +102,6 @@ services:
104102
volumes:
105103
- ./apps:/app/apps
106104
- ./config:/app/config
107-
- ./templates:/app/templates
108105
- ./static:/app/static
109106
- ./manage.py:/app/manage.py
110107
env_file:

frontend/src/components/BookFilters.tsx

Lines changed: 27 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,44 @@
1-
import { Search } from 'lucide-react';
21
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
3-
import { Input } from '@/components/ui/input';
4-
import { Button } from '@/components/ui/button';
52
import {
63
Select,
74
SelectContent,
85
SelectItem,
96
SelectTrigger,
107
SelectValue,
118
} from '@/components/ui/select';
12-
import { useState } from 'react';
13-
import type { Publisher, Tag } from '@/types';
9+
import type { Author, Publisher, Tag } from '@/types';
1410

1511
interface BookFiltersProps {
12+
authors: Author[];
1613
publishers: Publisher[];
1714
tags: Tag[];
18-
onSearch: (query: string) => void;
15+
onAuthorChange: (authorId: string) => void;
1916
onCategoryChange: (category: string) => void;
2017
onPublisherChange: (publisherId: string) => void;
2118
onSortChange: (sort: string) => void;
2219
}
2320

2421
export default function BookFilters({
22+
authors,
2523
publishers,
2624
tags,
27-
onSearch,
25+
onAuthorChange,
2826
onCategoryChange,
2927
onPublisherChange,
3028
onSortChange,
3129
}: BookFiltersProps) {
32-
const [searchQuery, setSearchQuery] = useState('');
33-
34-
const handleSearch = (e: React.FormEvent) => {
35-
e.preventDefault();
36-
onSearch(searchQuery);
37-
};
3830

3931
return (
4032
<Card className="mb-8">
4133
<CardHeader>
4234
<CardTitle>Фильтр книг</CardTitle>
4335
</CardHeader>
4436
<CardContent>
45-
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
46-
<div className="space-y-2">
47-
<label className="text-sm font-medium">Поиск</label>
48-
<form onSubmit={handleSearch} className="flex space-x-2">
49-
<Input
50-
type="text"
51-
placeholder="Поиск книг, авторов..."
52-
value={searchQuery}
53-
onChange={(e) => setSearchQuery(e.target.value)}
54-
/>
55-
<Button type="submit" size="sm">
56-
<Search className="h-4 w-4" />
57-
</Button>
58-
</form>
59-
</div>
60-
37+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
6138
<div className="space-y-2">
62-
<label className="text-sm font-medium">Теги</label>
39+
<label className="text-sm font-medium">Тег</label>
6340
<Select onValueChange={onCategoryChange}>
64-
<SelectTrigger>
41+
<SelectTrigger className="w-full">
6542
<SelectValue placeholder="Все теги" />
6643
</SelectTrigger>
6744
<SelectContent>
@@ -75,10 +52,27 @@ export default function BookFilters({
7552
</Select>
7653
</div>
7754

55+
<div className="space-y-2">
56+
<label className="text-sm font-medium">Автор</label>
57+
<Select onValueChange={onAuthorChange}>
58+
<SelectTrigger className="w-full">
59+
<SelectValue placeholder="Все авторы" />
60+
</SelectTrigger>
61+
<SelectContent>
62+
<SelectItem value="all">Все авторы</SelectItem>
63+
{authors.map((author) => (
64+
<SelectItem key={author.id} value={author.id.toString()}>
65+
{author.first_name} {author.last_name}
66+
</SelectItem>
67+
))}
68+
</SelectContent>
69+
</Select>
70+
</div>
71+
7872
<div className="space-y-2">
7973
<label className="text-sm font-medium">Издатель</label>
8074
<Select onValueChange={onPublisherChange}>
81-
<SelectTrigger>
75+
<SelectTrigger className="w-full">
8276
<SelectValue placeholder="Все издатели" />
8377
</SelectTrigger>
8478
<SelectContent>
@@ -95,7 +89,7 @@ export default function BookFilters({
9589
<div className="space-y-2">
9690
<label className="text-sm font-medium">Сортировать по</label>
9791
<Select defaultValue="-created" onValueChange={onSortChange}>
98-
<SelectTrigger>
92+
<SelectTrigger className="w-full">
9993
<SelectValue placeholder="Новейшие" />
10094
</SelectTrigger>
10195
<SelectContent>

0 commit comments

Comments
 (0)