diff --git a/apps/blog/admin.py b/apps/blog/admin.py index f73d5ded..e132e362 100644 --- a/apps/blog/admin.py +++ b/apps/blog/admin.py @@ -1,6 +1,12 @@ from django.contrib import admin -from apps.blog.models import Blog, Category +from apps.blog.models import Blog, Category, Comment + + +@admin.register(Blog) +class BlogAdmin(admin.ModelAdmin): + list_display = ("title", "enabled") + -admin.site.register(Blog) admin.site.register(Category) +admin.site.register(Comment) diff --git a/apps/blog/migrations/0002_blog_enabled.py b/apps/blog/migrations/0002_blog_enabled.py new file mode 100644 index 00000000..4bba9039 --- /dev/null +++ b/apps/blog/migrations/0002_blog_enabled.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.18 on 2026-08-02 11:23 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="blog", + name="enabled", + field=models.BooleanField(default=True), + ), + ] diff --git a/apps/blog/migrations/0003_comment.py b/apps/blog/migrations/0003_comment.py new file mode 100644 index 00000000..d93dae97 --- /dev/null +++ b/apps/blog/migrations/0003_comment.py @@ -0,0 +1,21 @@ +# Generated by Django 4.2.18 on 2026-08-02 12:36 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0002_blog_enabled"), + ] + + operations = [ + migrations.CreateModel( + name="Comment", + fields=[ + ("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")), + ("text", models.TextField()), + ("blog", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="blog.blog")), + ], + ), + ] diff --git a/apps/blog/migrations/0004_alter_comment_blog.py b/apps/blog/migrations/0004_alter_comment_blog.py new file mode 100644 index 00000000..8da077f6 --- /dev/null +++ b/apps/blog/migrations/0004_alter_comment_blog.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.18 on 2026-08-02 15:32 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("blog", "0003_comment"), + ] + + operations = [ + migrations.AlterField( + model_name="comment", + name="blog", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, related_name="comments", to="blog.blog" + ), + ), + ] diff --git a/apps/blog/models.py b/apps/blog/models.py index 141c4c26..422130a4 100644 --- a/apps/blog/models.py +++ b/apps/blog/models.py @@ -14,3 +14,9 @@ class Blog(models.Model): body = models.TextField() posted = models.DateField(db_index=True, auto_now_add=True) category = models.ForeignKey(Category, on_delete=models.CASCADE) + enabled = models.BooleanField(default=True) + + +class Comment(models.Model): + text = models.TextField() + blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name="comments") diff --git a/apps/blog/serializers.py b/apps/blog/serializers.py index 9137b4c3..ebf83dc3 100644 --- a/apps/blog/serializers.py +++ b/apps/blog/serializers.py @@ -1,6 +1,6 @@ from rest_framework import serializers -from apps.blog.models import Blog, Category +from apps.blog.models import Blog, Category, Comment class CategorySerializer(serializers.ModelSerializer): @@ -9,7 +9,15 @@ class Meta: fields = "__all__" +class CommentSerializer(serializers.ModelSerializer): + class Meta: + model = Comment + fields = "__all__" + + class BlogSerializer(serializers.ModelSerializer): + comments = CommentSerializer(many=True, read_only=True) + class Meta: model = Blog fields = "__all__" diff --git a/apps/blog/urls.py b/apps/blog/urls.py index 402081cf..5bf6ef83 100644 --- a/apps/blog/urls.py +++ b/apps/blog/urls.py @@ -1,7 +1,7 @@ from django.urls import path from rest_framework.routers import DefaultRouter -from apps.blog.views import BlogItemView, BlogListView, CategoryViewSet +from apps.blog.views import BlogCreateView, BlogItemView, BlogListView, CategoryViewSet, CommentCreateView router = DefaultRouter(trailing_slash=False) router.register( @@ -13,5 +13,7 @@ urlpatterns = [ path("blog", BlogListView.as_view(), name="blog_list"), path("blog/", BlogItemView.as_view(), name="blog_item"), + path("blog/create", BlogCreateView.as_view(), name="blog_create"), + path("comment/create", CommentCreateView.as_view(), name="comment_create"), *router.urls, ] diff --git a/apps/blog/views.py b/apps/blog/views.py index a9d39b23..ef45d35a 100644 --- a/apps/blog/views.py +++ b/apps/blog/views.py @@ -4,8 +4,8 @@ from rest_framework.request import Request from rest_framework.response import Response -from apps.blog.models import Blog, Category -from apps.blog.serializers import BlogSerializer, CategorySerializer +from apps.blog.models import Blog, Category, Comment +from apps.blog.serializers import BlogSerializer, CategorySerializer, CommentSerializer from apps.common.permissions import ReadOnly @@ -30,3 +30,32 @@ class BlogItemView(GenericAPIView): def get(self, request: Request, pk: int) -> Response: blog: Blog = get_object_or_404(Blog.objects.all(), pk=pk) return Response(self.get_serializer(blog).data) + + +class BlogCreateView(GenericAPIView): + serializer_class = BlogSerializer + permission_classes = (IsAuthenticated,) + + def post(self, request: Request) -> Response: + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + + blog = Blog.objects.create(**serializer.validated_data) + + return Response(self.serializer_class(blog).data) + + +class CommentCreateView(GenericAPIView): + serializer_class = CommentSerializer + permission_classes = (IsAuthenticated,) + + def post(self, request: Request) -> Response: + serializer = self.serializer_class(data=request.data) + serializer.is_valid(raise_exception=True) + + comment = Comment.objects.create( + blog_id=serializer.validated_data["blog"].id, + text=serializer.validated_data["text"], + ) + + return Response(self.serializer_class(comment).data)