diff --git a/django_rest_sample/__init__.py b/django_rest_sample/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..9c0f75627493779a57ce48f0b76e35cc87b8984a 100644 --- a/django_rest_sample/__init__.py +++ b/django_rest_sample/__init__.py @@ -0,0 +1,3 @@ +import pymysql + +pymysql.install_as_MySQLdb() diff --git a/django_rest_sample/settings.py b/django_rest_sample/settings.py index a6dc00d37c5b630e939cc8df79bf81cf1f3b86d5..2220d45d46327281fd6e647766e9d90e7d3ae7c9 100644 --- a/django_rest_sample/settings.py +++ b/django_rest_sample/settings.py @@ -15,7 +15,6 @@ import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) - # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ @@ -27,7 +26,6 @@ DEBUG = True ALLOWED_HOSTS = [] - # Application definition INSTALLED_APPS = [ @@ -37,8 +35,9 @@ INSTALLED_APPS = [ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'library_app.apps.LibraryAppConfig', - 'rest_framework' + # 'library_app.apps.LibraryAppConfig', + 'rest_framework', + 'library_app' ] MIDDLEWARE = [ @@ -72,18 +71,16 @@ TEMPLATES = [ WSGI_APPLICATION = 'django_rest_sample.wsgi.application' - # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), - } + 'NAME': os.path.join(BASE_DIR, 'db.sqlite3') + }, } - # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators @@ -102,7 +99,6 @@ AUTH_PASSWORD_VALIDATORS = [ }, ] - # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ @@ -116,14 +112,29 @@ USE_L10N = True USE_TZ = True - # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' - REST_FRAMEWORK = { 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination', 'PAGE_SIZE': 100 } + +# 限流操作,减轻服务器压力 +REST_FRAMEWORK = { + 'DEFAULT_THROTTLE_CLASSES': [ + 'rest_framework.throttling.AnonRateThrottle', + 'rest_framework.throttling.UserRateThrottle' + ], + 'DEFAULT_THROTTLE_RATES': { + 'anon': '3/second', + 'user': '10/second' + }, + 'DEFAULT_AUTHENTICATION_CLASSES': ( + 'rest_framework.authentication.SessionAuthentication', + 'rest_framework.authentication.BasicAuthentication', + ), + 'DEFAULT_SCHEMA_CLASS': 'rest_framework.schemas.coreapi.AutoSchema' +} diff --git a/django_rest_sample/urls.py b/django_rest_sample/urls.py index 05fb841da371ccc55cbe81f3dacc6687bba81312..e22b06f8fcc382fd0b963fb47e3e96d5fb1ce3eb 100644 --- a/django_rest_sample/urls.py +++ b/django_rest_sample/urls.py @@ -14,10 +14,10 @@ Including another URLconf 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin -from django.urls import path +from django.urls import path, include from rest_framework import routers -from library_app.views import BookViewSet, CategoryViewSet, AuthorViewSet +from library_app.views import BookViewSet, CategoryViewSet, AuthorViewSet, LibraryStatsViewSet router = routers.DefaultRouter() router.register(r'books', BookViewSet) @@ -27,4 +27,8 @@ urlpatterns = router.urls urlpatterns += [ path('admin/', admin.site.urls), + path('library/stats/', LibraryStatsViewSet.as_view({'get': 'stats'}), name='library-stats'), ] +""" +访问接口:http://127.0.0.1:8000/library/stats/ +""" \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..3fdd405cc80047c8fb9ede25aea51f0c2f9cca7c --- /dev/null +++ b/dockerfile @@ -0,0 +1,37 @@ +# 使用基础镜像 +FROM python:3.10 + +COPY . /django-restframework-test + +# 设置工作目录 +WORKDIR /django-restframework-test + + + + +COPY ./requirements.txt /app/requirements.txt + +# 安装项目依赖 +RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple + + + + +#RUN apt-get update && apt-get install -y mysql-client +RUN pip install mysqlclient + + + + + +# 运行数据库迁移 +RUN python /django-restframework-test/django-restframework-test/manage.py migrate + +# 设置环境变量 +#ENV DJANGO_SETTINGS_MODULE=myproject.settings.production + +# 暴露端口 +EXPOSE 9000 + +# 运行 Django 项目 +CMD ["python", "/django-restframework-test/django-restframework-test/manage.py", "runserver", "0.0.0.0:9000"] diff --git a/library_app/__init__.py b/library_app/__init__.py index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c45523b2f83d4ee6d8742e97d5c9f9775f01cae0 100644 --- a/library_app/__init__.py +++ b/library_app/__init__.py @@ -0,0 +1,2 @@ +import pymysql +pymysql.install_as_MySQLdb() \ No newline at end of file diff --git a/library_app/migrations/0002_alter_book_title.py b/library_app/migrations/0002_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..655e1a31bac93be274a76971564cc9bdc3268de2 --- /dev/null +++ b/library_app/migrations/0002_alter_book_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-08-23 16:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='books'), + ), + ] diff --git a/library_app/migrations/0003_alter_book_title.py b/library_app/migrations/0003_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..bd170c75b84aaf452d696072269bde6722f03d24 --- /dev/null +++ b/library_app/migrations/0003_alter_book_title.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.12 on 2023-08-23 17:06 +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0002_alter_book_title'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/library_app/migrations/0004_alter_book_title.py b/library_app/migrations/0004_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..06d9bdf68fd7f0df14a00797fd62955619c430d9 --- /dev/null +++ b/library_app/migrations/0004_alter_book_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-08-23 17:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0003_alter_book_title'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, max_length=255), + ), + ] diff --git a/library_app/migrations/0005_alter_book_title.py b/library_app/migrations/0005_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..7ccde116f94f77ad5799f88d486d1aac4f8e71bb --- /dev/null +++ b/library_app/migrations/0005_alter_book_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-08-23 17:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0004_alter_book_title'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/library_app/migrations/0006_alter_book_title.py b/library_app/migrations/0006_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..28c4c09837e2391e7e88a310746b210b3484103c --- /dev/null +++ b/library_app/migrations/0006_alter_book_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-08-23 17:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0005_alter_book_title'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, default='Untitled', max_length=255, null=True), + ), + ] diff --git a/library_app/migrations/0007_alter_book_title.py b/library_app/migrations/0007_alter_book_title.py new file mode 100644 index 0000000000000000000000000000000000000000..29601a74294f924ae0aa97b05464760c23f984a1 --- /dev/null +++ b/library_app/migrations/0007_alter_book_title.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.12 on 2023-08-22 04:56 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0006_alter_book_title'), + ] + + operations = [ + migrations.AlterField( + model_name='book', + name='title', + field=models.CharField(blank=True, max_length=255, null=True), + ), + ] diff --git a/library_app/migrations/0008_delete_book.py b/library_app/migrations/0008_delete_book.py new file mode 100644 index 0000000000000000000000000000000000000000..748985d78a6be7104ac0ce40dd3dd4cf1e3c65d3 --- /dev/null +++ b/library_app/migrations/0008_delete_book.py @@ -0,0 +1,16 @@ +# Generated by Django 3.2.12 on 2023-08-22 05:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0007_alter_book_title'), + ] + + operations = [ + migrations.DeleteModel( + name='Book', + ), + ] diff --git a/library_app/migrations/0009_book.py b/library_app/migrations/0009_book.py new file mode 100644 index 0000000000000000000000000000000000000000..6a7636390dd41a6aafc9ce9ca4cae52302e728f8 --- /dev/null +++ b/library_app/migrations/0009_book.py @@ -0,0 +1,26 @@ +# Generated by Django 3.2.12 on 2023-08-22 06:00 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0008_delete_book'), + ] + + operations = [ + migrations.CreateModel( + name='Book', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('title', models.CharField(blank=True, max_length=255, null=True)), + ('owned', models.IntegerField()), + ('price', models.FloatField()), + ('publish_date', models.DateField()), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library_app.author')), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='library_app.category')), + ], + ), + ] diff --git a/library_app/models.py b/library_app/models.py index 4efae69f4620109b96b6cacc7c3809592f8bbc5c..18e8ccd0a25b63768be701da7823291ab9800952 100644 --- a/library_app/models.py +++ b/library_app/models.py @@ -1,16 +1,7 @@ from django.db import models -class Book(models.Model): - title = models.CharField(max_length=255, null=True, blank=True) - category = models.ForeignKey('Category', on_delete=models.CASCADE) - author = models.ForeignKey('Author', on_delete=models.CASCADE) - owned = models.IntegerField() - price = models.FloatField() - publish_date = models.DateField() - def __str__(self): - return self.title class Author(models.Model): name = models.CharField(max_length=255, null=True, blank=True) @@ -24,3 +15,15 @@ class Category(models.Model): def __str__(self): return self.title + +# 这里原本用的是2.2的版本,现在我用的是3.2版本,所以改了一下写法,不然就会在访问的时候会报错 +class Book(models.Model): + title = models.CharField(max_length=255,null=True, blank=True) + category = models.ForeignKey(Category, on_delete=models.CASCADE) + author = models.ForeignKey(Author, on_delete=models.CASCADE) + owned = models.IntegerField() + price = models.FloatField() + publish_date = models.DateField() + + def __str__(self): + return self.title diff --git a/library_app/serializers.py b/library_app/serializers.py index 849aea1f7cfdf51d36afe7743319ac57b0688bc5..66b92088c8cd8f7defa66ae75e0c8f6f459c87bf 100644 --- a/library_app/serializers.py +++ b/library_app/serializers.py @@ -3,12 +3,17 @@ from rest_framework import serializers from library_app.models import Category, Book, Author - class AuthorSerializer(serializers.ModelSerializer): class Meta: model = Author fields = "__all__" +# 移动到了这里 +class CategorySerializer(serializers.ModelSerializer): + class Meta: + model = Category + fields = "__all__" + class BookReadSerializer(serializers.ModelSerializer): category = CategorySerializer() author = AuthorSerializer() @@ -17,12 +22,18 @@ class BookReadSerializer(serializers.ModelSerializer): model = Book fields = '__all__' + class BookWriteSerializer(serializers.ModelSerializer): class Meta: - model = Book - fields = "__all__" + model = Book + fields = '__all__' -class CategorySerializer(serializers.ModelSerializer): - class Meta: - model = Category - fields = "__all__" + + + + +# 一开始这里是报错的,因为在上面要用到,所以我移动了一下位置 +# class CategorySerializer(serializers.ModelSerializer): +# class Meta: +# model = Category +# fields = "__all__" diff --git a/library_app/views.py b/library_app/views.py index da56ab7fe376dc3d778ee7e3d280b4971270631d..d48e4ae081c6d84d7a73315369c35ecc279107a5 100644 --- a/library_app/views.py +++ b/library_app/views.py @@ -1,22 +1,74 @@ -from rest_framework import viewsets +from django.db.models import Count, Sum +from rest_framework import viewsets, status +from rest_framework.decorators import action +from rest_framework.response import Response +from rest_framework.viewsets import ModelViewSet from library_app.models import Category, Author, Book from library_app.serializers import CategorySerializer, AuthorSerializer, BookReadSerializer, BookWriteSerializer -class BookViewSet(viewsets.ModelViewSet): +class BookViewSet(ModelViewSet): queryset = Book.objects.all() + # print(queryset) + + """ + 一开始通过测试发现一开始是访问不了这个重写的视图函数: + """ def get_serializer_class(self): - if self.request.method in ['GET', 'HEAD']: + # if self.request.method in ['GET', 'HEAD']: + # return BookReadSerializer + # else: + # + # return BookWriteSerializer + """ + 这里我优化了一下适应django3.2版本 + 添加数据现在要借助第三方测试工具比如postman + :return: + """ + if self.action in ['list']: return BookReadSerializer - else: - return BookWriteSerializer + return BookWriteSerializer + + +# 自己写的功能 +""" +思路分析: + 计算图书总数 + 计算图书的总成本。这样可以提供图书馆的基本统计 + 获取每位作者拥有的图书数量,并按照数量进行降序排序。这样可以找到拥有最多书籍的作者,提供给用户更加具体的统计信息 + 获取每个类别拥有的图书数量,并按照数量进行降序排序。这样可以找到拥有最多书籍的类别,也提供给用户更加具体的统计信息。 + +""" + + +class LibraryStatsViewSet(viewsets.ViewSet): + @action(detail=False, methods=['get']) + def stats(self, request): + book_count = Book.objects.count() + total_cost = Book.objects.aggregate(total_cost=Sum('price')).get('total_cost', 0) + author_counts = Author.objects.annotate(book_count=Count('book')).order_by('-book_count') + category_counts = Category.objects.annotate(book_count=Count('book')).order_by('-book_count') + + most_books_author = author_counts.first() if author_counts else None + most_books_category = category_counts.first() if category_counts else None + + data = { + 'book_count': book_count, + 'total_cost': total_cost, + 'most_books_author': AuthorSerializer(most_books_author).data if most_books_author else None, + 'most_books_category': CategorySerializer(most_books_category).data if most_books_category else None + } + print(data) + + return Response(data) + class CategoryViewSet(viewsets.ModelViewSet): queryset = Category.objects.all() serializer_class = CategorySerializer - + class AuthorViewSet(viewsets.ModelViewSet): queryset = Author.objects.all() diff --git a/requirements.txt b/requirements.txt index 6e5370e2b9bb93b069cbcb6632a22ee0bbfc2258..fd776b04496dba70a456a2dfaa4827c1f3cf3229 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,7 @@ -django -djangorestframework +asgiref==3.7.2 +Django==3.2 +djangorestframework==3.14.0 +PyMySQL==1.1.0 +pytz==2023.3 +sqlparse==0.4.4 +typing-extensions==4.7.0