From 3909b6d21f515470e3fc6d8c9dc17829f18db84b Mon Sep 17 00:00:00 2001 From: Parsa Nazer Date: Wed, 29 Jan 2025 15:57:18 +0330 Subject: [PATCH] blog model and view and url and route --- backend/blog/__init__.py | 0 backend/blog/admin.py | 27 +++++++++++++ backend/blog/apps.py | 6 +++ backend/blog/migrations/0001_initial.py | 38 +++++++++++++++++ backend/blog/migrations/__init__.py | 0 backend/blog/models.py | 38 +++++++++++++++++ backend/blog/serializers.py | 13 ++++++ backend/blog/tests.py | 3 ++ backend/blog/urls.py | 7 ++++ backend/blog/views.py | 54 +++++++++++++++++++++++++ backend/core/settings.py | 1 + backend/core/urls.py | 1 + 12 files changed, 188 insertions(+) create mode 100644 backend/blog/__init__.py create mode 100644 backend/blog/admin.py create mode 100644 backend/blog/apps.py create mode 100644 backend/blog/migrations/0001_initial.py create mode 100644 backend/blog/migrations/__init__.py create mode 100644 backend/blog/models.py create mode 100644 backend/blog/serializers.py create mode 100644 backend/blog/tests.py create mode 100644 backend/blog/urls.py create mode 100644 backend/blog/views.py diff --git a/backend/blog/__init__.py b/backend/blog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/blog/admin.py b/backend/blog/admin.py new file mode 100644 index 0000000..07a907c --- /dev/null +++ b/backend/blog/admin.py @@ -0,0 +1,27 @@ +from django.contrib import admin +from .models import * +from unfold.admin import ModelAdmin + +from import_export.admin import ImportExportModelAdmin +from unfold.contrib.import_export.forms import ExportForm, ImportForm, SelectableFieldsExportForm +from unfold.contrib.forms.widgets import ArrayWidget, WysiwygWidget +from django.contrib.postgres.fields import ArrayField + + +@admin.register(BlogModel) +class BlogModelAdmin(ModelAdmin, ImportExportModelAdmin): + import_form_class = ImportForm + export_form_class = ExportForm + + + compressed_fields = True + warn_unsaved_form = True + + formfield_overrides = { + models.TextField: { + "widget": WysiwygWidget, + }, + ArrayField: { + "widget": ArrayWidget, + } + } diff --git a/backend/blog/apps.py b/backend/blog/apps.py new file mode 100644 index 0000000..94788a5 --- /dev/null +++ b/backend/blog/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BlogConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'blog' diff --git a/backend/blog/migrations/0001_initial.py b/backend/blog/migrations/0001_initial.py new file mode 100644 index 0000000..f4718fc --- /dev/null +++ b/backend/blog/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# Generated by Django 5.1.2 on 2025-01-29 12:17 + +import django.db.models.deletion +import django.utils.timezone +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('product', '0004_alter_subcategorymodel_parent'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='BlogModel', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(max_length=200)), + ('slug', models.SlugField(blank=True, max_length=200, unique=True)), + ('content', models.TextField()), + ('summery', models.TextField()), + ('created_at', models.DateTimeField(default=django.utils.timezone.now, editable=False)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('is_published', models.BooleanField(default=False)), + ('cover_image', models.ImageField(upload_to='blog_covers/')), + ('views', models.PositiveIntegerField(default=0)), + ('meta_description', models.CharField(help_text='این فیلد را حتما پر کنید', max_length=300)), + ('meta_keywords', models.CharField(help_text='این فیلد را حتما پر کنید', max_length=300)), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blogs', to=settings.AUTH_USER_MODEL)), + ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='blogs', to='product.subcategorymodel')), + ], + ), + ] diff --git a/backend/blog/migrations/__init__.py b/backend/blog/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/blog/models.py b/backend/blog/models.py new file mode 100644 index 0000000..103521a --- /dev/null +++ b/backend/blog/models.py @@ -0,0 +1,38 @@ +from django.db import models +from account.models import User +from django.utils.text import slugify +from django.utils.timezone import now +from product.models import SubCategoryModel + +class BlogModel(models.Model): + author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blogs') + title = models.CharField(max_length=200) + slug = models.SlugField(max_length=200, unique=True, blank=True) + content = models.TextField() + summery = models.TextField() + category = models.ForeignKey(SubCategoryModel, on_delete=models.SET_NULL, null=True, related_name='blogs') + created_at = models.DateTimeField(default=now, editable=False) + updated_at = models.DateTimeField(auto_now=True) + is_published = models.BooleanField(default=False) + cover_image = models.ImageField(upload_to='blog_covers/', blank=True) + views = models.PositiveIntegerField(default=0) + meta_description = models.CharField(max_length=300, help_text='این فیلد را حتما پر کنید') + meta_keywords = models.CharField(max_length=300, help_text='این فیلد را حتما پر کنید') + def save(self, *args, **kwargs): + if not self.slug: + self.slug = slugify(self.title) + super().save(*args, **kwargs) + + def __str__(self): + return self.title + + + +# class Comment(models.Model): +# blog = models.ForeignKey(Blog, on_delete=models.CASCADE, related_name='comments') +# user = models.ForeignKey(User, on_delete=models.CASCADE) +# content = models.TextField() +# created_at = models.DateTimeField(default=now) + +# def __str__(self): +# return f'Comment by {self.user} on {self.blog}' \ No newline at end of file diff --git a/backend/blog/serializers.py b/backend/blog/serializers.py new file mode 100644 index 0000000..9a8cfff --- /dev/null +++ b/backend/blog/serializers.py @@ -0,0 +1,13 @@ +from rest_framework import serializers +from .models import BlogModel + +class BlogSerilizer(serializers.ModelSerializer): + class Meta: + model = BlogModel + fields = "__all__" + + +class AllBlogSerilizer(serializers.ModelSerializer): + class Meta: + model = BlogModel + fields = "__all__" \ No newline at end of file diff --git a/backend/blog/tests.py b/backend/blog/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/blog/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/blog/urls.py b/backend/blog/urls.py new file mode 100644 index 0000000..ca90f37 --- /dev/null +++ b/backend/blog/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from . import views + +urlpatterns = [ + path('all', views.AllBlogView.as_view(), name='product-chat-view'), + path('', views.BlogView.as_view(), name='product-chat-view'), +] \ No newline at end of file diff --git a/backend/blog/views.py b/backend/blog/views.py new file mode 100644 index 0000000..bf8b968 --- /dev/null +++ b/backend/blog/views.py @@ -0,0 +1,54 @@ +from django.shortcuts import render +from rest_framework.views import APIView, Response +from rest_framework import status +from .models import BlogModel +from .serializers import AllBlogSerilizer, BlogSerilizer +from django.shortcuts import get_object_or_404 +from utils.pagination import StructurePagination +from drf_spectacular.utils import extend_schema, OpenApiParameter, OpenApiTypes + + + +class AllBlogView(APIView): + authentication_classes = [] + serializer_class = AllBlogSerilizer + pagination_class = StructurePagination + @extend_schema( + parameters=[ + OpenApiParameter( + name="limit", + description="لیمیتش", + required=False, + type=OpenApiTypes.INT, + ), + OpenApiParameter( + name="offset", + description="افستش", + required=False, + type=OpenApiTypes.INT, + ) + ], + responses={ + 200: AllBlogSerilizer(many=True), + 404: OpenApiTypes.OBJECT, + }, + ) + def get(self, request): + blogs = BlogModel.objects.filter(is_published=True) + paginator = self.pagination_class() + paginated_blogs = paginator.paginate_queryset(blogs, request) + blog_ser = self.serializer_class(instance=paginated_blogs, many=True, context={'request': request}) + return paginator.get_paginated_response(blog_ser.data) + + + +class BlogView(APIView): + authentication_classes = [] + serializer_class = BlogSerilizer + def get(self, request, pk): + blog = get_object_or_404(BlogModel, pk=pk) + if blog.is_published: + blog_ser = self.serializer_class(instance=blog, context={'request': request}) + return Response(blog_ser.data, status=status.HTTP_200_OK) + else: + return Response({'detail': 'object with the given id does not exiest or its not published yet'}, status=status.HTTP_404_NOT_FOUND) \ No newline at end of file diff --git a/backend/core/settings.py b/backend/core/settings.py index 7595fbe..f9e4201 100644 --- a/backend/core/settings.py +++ b/backend/core/settings.py @@ -111,6 +111,7 @@ INSTALLED_APPS = [ 'chat', 'order', 'home', + 'blog', ] MIDDLEWARE = [ diff --git a/backend/core/urls.py b/backend/core/urls.py index c2470c3..b5e2f05 100644 --- a/backend/core/urls.py +++ b/backend/core/urls.py @@ -24,6 +24,7 @@ urlpatterns = [ path('accounts/', include('account.urls')), path('chat/', include('chat.urls')), path('tickets/', include('ticket.urls')), + path('blogs/', include('blog.urls')), path('', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'), ]