mirror of
https://github.com/Alexander-D-Karpov/akarpov
synced 2024-11-22 13:16:33 +03:00
added blog api, updated user api
This commit is contained in:
parent
e403a29cda
commit
13638466a2
86
akarpov/blog/api/serializers.py
Normal file
86
akarpov/blog/api/serializers.py
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
from drf_spectacular.utils import extend_schema_field
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
from akarpov.blog.models import Comment, Post, Tag
|
||||||
|
from akarpov.common.api import RecursiveField
|
||||||
|
from akarpov.users.api.serializers import UserPublicInfoSerializer
|
||||||
|
|
||||||
|
|
||||||
|
class TagSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = Tag
|
||||||
|
fields = ["name", "color"]
|
||||||
|
|
||||||
|
|
||||||
|
class PostSerializer(serializers.ModelSerializer):
|
||||||
|
creator = UserPublicInfoSerializer()
|
||||||
|
main_tag = serializers.SerializerMethodField(method_name="get_h_tag")
|
||||||
|
url = serializers.HyperlinkedIdentityField(
|
||||||
|
view_name="api:blog:post", lookup_field="slug"
|
||||||
|
)
|
||||||
|
|
||||||
|
@extend_schema_field(TagSerializer)
|
||||||
|
def get_h_tag(self, obj):
|
||||||
|
return TagSerializer(many=False).to_representation(instance=obj.h_tags()[0])
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = [
|
||||||
|
"title",
|
||||||
|
"url",
|
||||||
|
"main_tag",
|
||||||
|
"image_cropped",
|
||||||
|
"summary",
|
||||||
|
"creator",
|
||||||
|
"post_views",
|
||||||
|
"rating",
|
||||||
|
"comment_count",
|
||||||
|
"short_link",
|
||||||
|
"created",
|
||||||
|
"edited",
|
||||||
|
]
|
||||||
|
extra_kwargs = {
|
||||||
|
"url": {"view_name": "api:blog:post", "lookup_field": "slug"},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class FullPostSerializer(PostSerializer):
|
||||||
|
"""note: body is returned as html for format"""
|
||||||
|
|
||||||
|
tags = TagSerializer(many=True)
|
||||||
|
comments = serializers.HyperlinkedIdentityField(
|
||||||
|
view_name="api:blog:post_comments", lookup_field="slug"
|
||||||
|
)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Post
|
||||||
|
fields = [
|
||||||
|
"title",
|
||||||
|
"image",
|
||||||
|
"tags",
|
||||||
|
"summary",
|
||||||
|
"creator",
|
||||||
|
"post_views",
|
||||||
|
"rating",
|
||||||
|
"comment_count",
|
||||||
|
"comments",
|
||||||
|
"short_link",
|
||||||
|
"created",
|
||||||
|
"edited",
|
||||||
|
]
|
||||||
|
|
||||||
|
extra_kwargs = {
|
||||||
|
"fio": {"required": False},
|
||||||
|
"username": {"required": False},
|
||||||
|
"is_agent": {"read_only": True},
|
||||||
|
"form": {"read_only": True},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CommentSerializer(serializers.ModelSerializer):
|
||||||
|
author = UserPublicInfoSerializer()
|
||||||
|
children = RecursiveField(many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = Comment
|
||||||
|
fields = ["author", "body", "created", "rating"]
|
17
akarpov/blog/api/urls.py
Normal file
17
akarpov/blog/api/urls.py
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
from django.urls import path
|
||||||
|
|
||||||
|
from akarpov.blog.api.views import (
|
||||||
|
GetPost,
|
||||||
|
ListCommentsSerializer,
|
||||||
|
ListMainPostsView,
|
||||||
|
ListPostsView,
|
||||||
|
)
|
||||||
|
|
||||||
|
app_name = "blog_api"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("", ListMainPostsView.as_view(), name="list_main"),
|
||||||
|
path("all/", ListPostsView.as_view(), name="list_all"),
|
||||||
|
path("<str:slug>", GetPost.as_view(), name="post"),
|
||||||
|
path("<str:slug>/comments", ListCommentsSerializer.as_view(), name="post_comments"),
|
||||||
|
]
|
50
akarpov/blog/api/views.py
Normal file
50
akarpov/blog/api/views.py
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
from rest_framework import generics
|
||||||
|
from rest_framework.generics import get_object_or_404
|
||||||
|
from rest_framework.permissions import AllowAny
|
||||||
|
|
||||||
|
from akarpov.blog.api.serializers import (
|
||||||
|
CommentSerializer,
|
||||||
|
FullPostSerializer,
|
||||||
|
PostSerializer,
|
||||||
|
)
|
||||||
|
from akarpov.blog.models import Post
|
||||||
|
from akarpov.blog.services import get_main_rating_posts
|
||||||
|
from akarpov.common.api import StandardResultsSetPagination
|
||||||
|
|
||||||
|
|
||||||
|
class ListMainPostsView(generics.ListAPIView):
|
||||||
|
serializer_class = PostSerializer
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
pagination_class = StandardResultsSetPagination
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return get_main_rating_posts()
|
||||||
|
|
||||||
|
|
||||||
|
class ListPostsView(generics.ListAPIView):
|
||||||
|
serializer_class = PostSerializer
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
pagination_class = StandardResultsSetPagination
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return Post.objects.all()
|
||||||
|
|
||||||
|
|
||||||
|
class GetPost(generics.RetrieveAPIView):
|
||||||
|
serializer_class = FullPostSerializer
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def get_object(self):
|
||||||
|
post = get_object_or_404(Post, slug=self.kwargs["slug"])
|
||||||
|
post.post_views += 1
|
||||||
|
post.save(update_fields=["post_views"])
|
||||||
|
return post
|
||||||
|
|
||||||
|
|
||||||
|
class ListCommentsSerializer(generics.ListAPIView):
|
||||||
|
serializer_class = CommentSerializer
|
||||||
|
permission_classes = [AllowAny]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
post = get_object_or_404(Post, slug=self.kwargs["slug"])
|
||||||
|
return post.comments.filter(parent__isnull=True)
|
|
@ -3,6 +3,8 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import Count
|
from django.db.models import Count
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
from drf_spectacular.utils import extend_schema_field
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
from akarpov.common.models import BaseImageModel
|
from akarpov.common.models import BaseImageModel
|
||||||
from akarpov.tools.shortener.models import ShortLinkModel
|
from akarpov.tools.shortener.models import ShortLinkModel
|
||||||
|
@ -41,9 +43,8 @@ def get_comments(self):
|
||||||
def h_tags(self):
|
def h_tags(self):
|
||||||
# TODO: add caching here
|
# TODO: add caching here
|
||||||
tags = (
|
tags = (
|
||||||
Tag.objects.all()
|
Tag.objects.filter(posts__id=self.id)
|
||||||
.annotate(num_posts=Count("posts"))
|
.annotate(num_posts=Count("posts"))
|
||||||
.filter(posts__id=self.id)
|
|
||||||
.order_by("-num_posts")
|
.order_by("-num_posts")
|
||||||
)
|
)
|
||||||
return tags
|
return tags
|
||||||
|
@ -57,6 +58,7 @@ def text(self):
|
||||||
return cleanhtml(self.body)
|
return cleanhtml(self.body)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
@extend_schema_field(serializers.CharField)
|
||||||
def summary(self):
|
def summary(self):
|
||||||
body = self.text
|
body = self.text
|
||||||
return body[:100] + "..." if len(body) > 100 else ""
|
return body[:100] + "..." if len(body) > 100 else ""
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
|
|
||||||
from akarpov.blog import models
|
from akarpov.blog import models
|
||||||
|
from akarpov.blog.models import Post
|
||||||
from akarpov.users.models import User
|
from akarpov.users.models import User
|
||||||
|
|
||||||
|
|
||||||
|
@ -52,3 +53,7 @@ def get_rating_bar(user: User, post):
|
||||||
+ f"""<form method="post" action="{url_down}" class="col-auto align-self-center"><button class="btn border-0
|
+ f"""<form method="post" action="{url_down}" class="col-auto align-self-center"><button class="btn border-0
|
||||||
btn-small"><i style="font-size: 1rem;" class="bi bi-arrow-down-circle"></i></button></form></div>"""
|
btn-small"><i style="font-size: 1rem;" class="bi bi-arrow-down-circle"></i></button></form></div>"""
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def get_main_rating_posts() -> [Post]:
|
||||||
|
return Post.objects.filter(creator__is_superuser=True)
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
from akarpov.blog.views import (
|
from akarpov.blog.views import (
|
||||||
comment,
|
comment,
|
||||||
|
main_post_list_view,
|
||||||
post_create_view,
|
post_create_view,
|
||||||
post_detail_view,
|
post_detail_view,
|
||||||
post_list_view,
|
post_list_view,
|
||||||
|
@ -12,7 +13,8 @@
|
||||||
|
|
||||||
app_name = "blog"
|
app_name = "blog"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", post_list_view, name="post_list"),
|
path("", main_post_list_view, name="post_list"),
|
||||||
|
path("all", post_list_view, name="all_posts_list"),
|
||||||
path("p/<str:slug>", post_detail_view, name="post"),
|
path("p/<str:slug>", post_detail_view, name="post"),
|
||||||
path("create/", post_create_view, name="post_create"),
|
path("create/", post_create_view, name="post_create"),
|
||||||
path("<str:slug>/edit", post_update_view, name="post_edit"),
|
path("<str:slug>/edit", post_update_view, name="post_edit"),
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
from akarpov.blog.forms import PostForm
|
from akarpov.blog.forms import PostForm
|
||||||
from akarpov.blog.models import Comment, Post, PostRating
|
from akarpov.blog.models import Comment, Post, PostRating
|
||||||
from akarpov.blog.services import get_rating_bar
|
from akarpov.blog.services import get_main_rating_posts, get_rating_bar
|
||||||
|
|
||||||
|
|
||||||
class PostDetailView(DetailView):
|
class PostDetailView(DetailView):
|
||||||
|
@ -34,6 +34,34 @@ def get_context_data(self, **kwargs):
|
||||||
post_detail_view = PostDetailView.as_view()
|
post_detail_view = PostDetailView.as_view()
|
||||||
|
|
||||||
|
|
||||||
|
class MainPostListView(ListView):
|
||||||
|
model = Post
|
||||||
|
template_name = "blog/list.html"
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
try:
|
||||||
|
if (
|
||||||
|
self.request.user.is_authenticated
|
||||||
|
and not self.request.user.is_superuser
|
||||||
|
):
|
||||||
|
posts = get_main_rating_posts() | Post.objects.filter(
|
||||||
|
creator=self.request.user
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
posts = get_main_rating_posts()
|
||||||
|
|
||||||
|
params = self.request.GET
|
||||||
|
if "tag" in params:
|
||||||
|
posts = posts.filter(tags__name=params["tag"])
|
||||||
|
return posts
|
||||||
|
|
||||||
|
except Post.DoesNotExist:
|
||||||
|
return Post.objects.none()
|
||||||
|
|
||||||
|
|
||||||
|
main_post_list_view = MainPostListView.as_view()
|
||||||
|
|
||||||
|
|
||||||
class PostListView(ListView):
|
class PostListView(ListView):
|
||||||
model = Post
|
model = Post
|
||||||
template_name = "blog/list.html"
|
template_name = "blog/list.html"
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from rest_framework import serializers
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,3 +18,9 @@ class BigResultsSetPagination(PageNumberPagination):
|
||||||
page_size = 100
|
page_size = 100
|
||||||
page_size_query_param = "page_size"
|
page_size_query_param = "page_size"
|
||||||
max_page_size = 1000
|
max_page_size = 1000
|
||||||
|
|
||||||
|
|
||||||
|
class RecursiveField(serializers.Serializer):
|
||||||
|
def to_representation(self, value):
|
||||||
|
serializer = self.parent.parent.__class__(value, context=self.context)
|
||||||
|
return serializer.data
|
||||||
|
|
0
akarpov/tools/api/__init__.py
Normal file
0
akarpov/tools/api/__init__.py
Normal file
7
akarpov/tools/api/urls.py
Normal file
7
akarpov/tools/api/urls.py
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
from django.urls import include, path
|
||||||
|
|
||||||
|
app_name = "tools"
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
path("qr/", include("akarpov.tools.qr.api.urls", namespace="qr")),
|
||||||
|
]
|
|
@ -25,7 +25,7 @@ def validate_token(self, token):
|
||||||
|
|
||||||
class UserPublicInfoSerializer(serializers.ModelSerializer):
|
class UserPublicInfoSerializer(serializers.ModelSerializer):
|
||||||
url = serializers.HyperlinkedIdentityField(
|
url = serializers.HyperlinkedIdentityField(
|
||||||
view_name="user_retrieve_username_api", lookup_field="username"
|
view_name="api:users:user_retrieve_username_api", lookup_field="username"
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
UserRetrieveViewSet,
|
UserRetrieveViewSet,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
app_name = "users_api"
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", UserListViewSet.as_view(), name="user_list_api"),
|
path("", UserListViewSet.as_view(), name="user_list_api"),
|
||||||
path(
|
path(
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
from akarpov.users.api.views import UserRegisterViewSet
|
from akarpov.users.api.views import UserRegisterViewSet
|
||||||
|
|
||||||
|
app_name = "api"
|
||||||
|
|
||||||
urlpatterns_v1 = [
|
urlpatterns_v1 = [
|
||||||
path(
|
path(
|
||||||
"auth/",
|
"auth/",
|
||||||
|
@ -17,11 +19,18 @@
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"users/",
|
"users/",
|
||||||
include("akarpov.users.api.urls"),
|
include("akarpov.users.api.urls", namespace="users"),
|
||||||
|
),
|
||||||
|
path(
|
||||||
|
"blog/",
|
||||||
|
include("akarpov.blog.api.urls", namespace="blog"),
|
||||||
),
|
),
|
||||||
path(
|
path(
|
||||||
"tools/",
|
"tools/",
|
||||||
include([path("qr/", include("akarpov.tools.qr.api.urls"))]),
|
include(
|
||||||
|
"akarpov.tools.api.urls",
|
||||||
|
namespace="tools",
|
||||||
|
),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user