diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..13566b81b018ad684f3a35fee301741b2734c8f4 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/django-restframework-test.iml b/.idea/django-restframework-test.iml new file mode 100644 index 0000000000000000000000000000000000000000..9323314a763a36a1e6146a1ee5ac93c282f36ce7 --- /dev/null +++ b/.idea/django-restframework-test.iml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000000000000000000000000000000000..cdff312489065acd641e40eeb4d258720859beb3 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,14 @@ + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000000000000000000000000000000000000..105ce2da2d6447d11dfe32bfb846c3d5b199fc99 --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000000000000000000000000000000000000..d1e22ecb89619a9c2dcf51a28d891a196d2462a0 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000000000000000000000000000000000000..1889bab237d621b9c36a18b3e1e08de49494f49b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000000000000000000000000000000000000..94a25f7f4cb416c083d265558da75d457237d671 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 42db1a76d95f421c660bd03c45f143554cb13386..023472b73a4bf841f92489ca058819f38c0c1bde 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/django_rest_sample/urls.py b/django_rest_sample/urls.py index 05fb841da371ccc55cbe81f3dacc6687bba81312..3e454f858e6730799a9b42bb264a9ad5af7748c5 100644 --- a/django_rest_sample/urls.py +++ b/django_rest_sample/urls.py @@ -17,12 +17,13 @@ from django.contrib import admin from django.urls import path from rest_framework import routers -from library_app.views import BookViewSet, CategoryViewSet, AuthorViewSet +from library_app.views import BookViewSet, CategoryViewSet, AuthorViewSet, SummaryViewSet router = routers.DefaultRouter() router.register(r'books', BookViewSet) router.register(r'categories', CategoryViewSet) router.register(r'authors', AuthorViewSet) +router.register(r'summary', SummaryViewSet) urlpatterns = router.urls urlpatterns += [ diff --git a/img.png b/img.png new file mode 100644 index 0000000000000000000000000000000000000000..25125ab5323237be2c8be95dacdc828b880a9701 Binary files /dev/null and b/img.png differ diff --git a/img_1.png b/img_1.png new file mode 100644 index 0000000000000000000000000000000000000000..29d232c2998895ca26506176b3e12d0d0733cc6d Binary files /dev/null and b/img_1.png differ diff --git a/img_2.png b/img_2.png new file mode 100644 index 0000000000000000000000000000000000000000..ab5087c8bca17f4ce7163d3eabbd8b4a2942522e Binary files /dev/null and b/img_2.png differ diff --git a/img_3.png b/img_3.png new file mode 100644 index 0000000000000000000000000000000000000000..5098e2127e794343d1b7ef6d287cca4069eb7ef2 Binary files /dev/null and b/img_3.png differ diff --git a/img_4.png b/img_4.png new file mode 100644 index 0000000000000000000000000000000000000000..53856486c9939bb734822823d843555c1ba89021 Binary files /dev/null and b/img_4.png differ diff --git a/img_5.png b/img_5.png new file mode 100644 index 0000000000000000000000000000000000000000..53856486c9939bb734822823d843555c1ba89021 Binary files /dev/null and b/img_5.png differ diff --git a/img_6.png b/img_6.png new file mode 100644 index 0000000000000000000000000000000000000000..320f1e61be722d4c3966b709a3756556614687fc Binary files /dev/null and b/img_6.png differ diff --git a/img_7.png b/img_7.png new file mode 100644 index 0000000000000000000000000000000000000000..2498415007e4de90fe34e38342ff11420b654246 Binary files /dev/null and b/img_7.png differ diff --git a/library_app/migrations/0002_summary.py b/library_app/migrations/0002_summary.py new file mode 100644 index 0000000000000000000000000000000000000000..6db5e5725acf1fa9b71ccaadc2168583b2fd0a37 --- /dev/null +++ b/library_app/migrations/0002_summary.py @@ -0,0 +1,23 @@ +# Generated by Django 2.2.5 on 2022-06-19 07:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Summary', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('sum_price', models.DecimalField(decimal_places=2, max_digits=20, verbose_name='书本总价')), + ('sum_owned', models.IntegerField(verbose_name='书本总数')), + ('most_author', models.ManyToManyField(to='library_app.Author', verbose_name='最多书作者')), + ('most_category', models.ManyToManyField(to='library_app.Category', verbose_name='最多书种类')), + ], + ), + ] diff --git a/library_app/migrations/0003_auto_20220620_1043.py b/library_app/migrations/0003_auto_20220620_1043.py new file mode 100644 index 0000000000000000000000000000000000000000..bfcfe967589cd6f3bf2937101cb8138fd6412b08 --- /dev/null +++ b/library_app/migrations/0003_auto_20220620_1043.py @@ -0,0 +1,33 @@ +# Generated by Django 2.2.5 on 2022-06-20 02:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('library_app', '0002_summary'), + ] + + operations = [ + migrations.RenameField( + model_name='summary', + old_name='most_author', + new_name='mostauthor', + ), + migrations.RenameField( + model_name='summary', + old_name='most_category', + new_name='mostcategory', + ), + migrations.RenameField( + model_name='summary', + old_name='sum_owned', + new_name='sumowned', + ), + migrations.RenameField( + model_name='summary', + old_name='sum_price', + new_name='sumprice', + ), + ] diff --git a/library_app/models.py b/library_app/models.py index 4efae69f4620109b96b6cacc7c3809592f8bbc5c..8c5f317679cfaba3fde2d16d3514af1301274e5b 100644 --- a/library_app/models.py +++ b/library_app/models.py @@ -4,7 +4,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) + author = models.ForeignKey('Author', on_delete=models.CASCADE) owned = models.IntegerField() price = models.FloatField() publish_date = models.DateField() @@ -12,15 +12,23 @@ class Book(models.Model): def __str__(self): return self.title + class Author(models.Model): name = models.CharField(max_length=255, null=True, blank=True) def __str__(self): - return self.name + return self.name if self.name else "null" + -class Category(models.Model): +class Category(models.Model): title = models.CharField(max_length=100, null=True, blank=True) def __str__(self): return self.title + +class Summary(models.Model): + sumprice = models.DecimalField(max_digits=20, verbose_name='书本总价', decimal_places=2) + sumowned = models.IntegerField(verbose_name='书本总数') + mostauthor = models.ManyToManyField(Author, verbose_name='最多书作者') + mostcategory = models.ManyToManyField(Category, verbose_name='最多书种类') diff --git a/library_app/serializers.py b/library_app/serializers.py index 849aea1f7cfdf51d36afe7743319ac57b0688bc5..45ddf242a0e99d2671353f12835bf95b66d5a78b 100644 --- a/library_app/serializers.py +++ b/library_app/serializers.py @@ -1,7 +1,7 @@ +from django.db.models import Count, Sum, F, DecimalField from rest_framework import serializers -from library_app.models import Category, Book, Author - +from library_app.models import Category, Book, Author, Summary class AuthorSerializer(serializers.ModelSerializer): @@ -9,20 +9,46 @@ class AuthorSerializer(serializers.ModelSerializer): model = Author fields = "__all__" -class BookReadSerializer(serializers.ModelSerializer): - category = CategorySerializer() - author = AuthorSerializer() - - class Meta: - model = Book - fields = '__all__' class BookWriteSerializer(serializers.ModelSerializer): class Meta: model = Book fields = "__all__" + class CategorySerializer(serializers.ModelSerializer): class Meta: model = Category fields = "__all__" + + +class BookReadSerializer(serializers.ModelSerializer): + category = CategorySerializer() + author = AuthorSerializer() + + class Meta: + model = Book + fields = '__all__' + + +class SummarySerializer(serializers.ModelSerializer): + sumprice = serializers.SerializerMethodField() + sumowned = serializers.SerializerMethodField() + mostauthor = serializers.SerializerMethodField() + mostcategory = serializers.SerializerMethodField() + def get_sumprice(self, obj): + my_dict1 = Book.objects.aggregate(sum_price=Sum(F('price') * F('owned'), output_field=DecimalField())) + return my_dict1.get('sum_price') + # return Book.objects.aggregate(sum_price=Sum(F('price') * F('owned'), output_field=DecimalField())) + def get_sumowned(self, obj): + my_dict2 = Book.objects.aggregate(sum_owned=Sum(F('owned'), output_field=DecimalField())) + return my_dict2.get('sum_owned') + def get_mostauthor(self, obj): + my_list1 = Book.objects.values('author').annotate(total=Count('author')) + return Author.objects.get(id=int(my_list1[0]['author'])).name + def get_mostcategory(self, obj): + my_list2 = Book.objects.values('category').annotate(total=Count('category')) + return Category.objects.get(id=int(my_list2[0]['category'])).title + class Meta: + model = Summary + fields = ['sumprice', 'sumowned', 'mostauthor', 'mostcategory'] diff --git a/library_app/views.py b/library_app/views.py index da56ab7fe376dc3d778ee7e3d280b4971270631d..dae38c521e6575d38914e45660fc75ca4f29ba61 100644 --- a/library_app/views.py +++ b/library_app/views.py @@ -1,7 +1,8 @@ from rest_framework import viewsets -from library_app.models import Category, Author, Book -from library_app.serializers import CategorySerializer, AuthorSerializer, BookReadSerializer, BookWriteSerializer +from library_app.models import Category, Author, Book, Summary +from library_app.serializers import CategorySerializer, AuthorSerializer, BookReadSerializer, BookWriteSerializer, \ + SummarySerializer class BookViewSet(viewsets.ModelViewSet): @@ -13,11 +14,17 @@ class BookViewSet(viewsets.ModelViewSet): else: return BookWriteSerializer + class CategoryViewSet(viewsets.ModelViewSet): queryset = Category.objects.all() serializer_class = CategorySerializer - + class AuthorViewSet(viewsets.ModelViewSet): queryset = Author.objects.all() serializer_class = AuthorSerializer + + +class SummaryViewSet(viewsets.ModelViewSet): + queryset = Summary.objects.all() + serializer_class = SummarySerializer diff --git "a/\345\274\200\345\217\221\347\254\224\350\256\260.md" "b/\345\274\200\345\217\221\347\254\224\350\256\260.md" new file mode 100644 index 0000000000000000000000000000000000000000..d98df5ab4e83bb012cc2e69cfad8521646c2df54 --- /dev/null +++ "b/\345\274\200\345\217\221\347\254\224\350\256\260.md" @@ -0,0 +1,115 @@ +# Django +[Django官方文档](https://docs.djangoproject.com/zh-hans/4.0/contents/) +
+[Django菜鸟教程](https://www.runoob.com/django/django-tutorial.html) +
+[Django B站教程](https://www.bilibili.com/video/BV1pX4y1F7oz?spm_id_from=333.337.search-card.all.click&vd_source=7c5f5a3ed2bc9ce87a5ea92d9baf1c25) +
+Django 是一个开放源代码的 Web 应用框架,采用了 MVT 的软件设计模式,即模型(Model),视图(View)和模板(Template),由Python写成。 +
+```python +View:视图是一个请求处理函数,它接收HTTP请求并返回HTTP响应。视图通过模型访问满足请求所需的数据,并将响应的格式委托给模板。 +Model:模型是定义应用程序数据结构的Python对象,并提供在数据库中管理(添加、修改、删除)和查询记录的机制。 +Templates:模板是定义文件(例如HTML页面)的结构或布局的文本文件,用于表示实际内容的占位符。一个视图可以使用HTML模板,从数据填充它动态地创建一个HTML页面模型。可以使用模板来定义任何类型的文件结构,不一定是HTML。 +``` +![img.png](img.png) +# Django REST framework +[Django REST framework官方文档(英)](https://www.django-rest-framework.org/) +
+[Django REST framework官方文档(中)](https://q1mi.github.io/Django-REST-framework-documentation/) +
+[Django REST framework B站教程](https://www.bilibili.com/video/BV1XR4y157rk?p=1&vd_source=7c5f5a3ed2bc9ce87a5ea92d9baf1c25) +
+Django REST framework是基于Django实现的一个RESTful风格API框架,能够帮助我们快速开发RESTful风格的API。 +```python +API是一个明确定义的接口,可以为其他软件提供特定服务。 +API是一个个具体的函数,一个确定的功能,一个获取后端数据的通道。 +API可以小到只包含一个单独的函数,也可以大到包含数以百计的类、方法、全局函数、数据类型、枚举类型和常量等。 +``` +# 修复错误 +## 错误① +``` +> pip install -r requirements.txt # 导入程序所需的依赖包 +> python manage.py makemigrations # 根据检测到的模型创建新的迁移 +> python manage.py migrate # 使数据库状态与当前模型集和迁移集同步 +> python manage.py runserver # 启用服务器 + -- server will be run on 127.0.0.1:8000 +``` +首先将代码clone到pycharm,在Terminal执行第二条语句后便发现第一条错误。 +![CategorySerializer没有定义就使用](img_1.png) +
+未定义名称CategorySerializer。 +![代码爆红位置](img_2.png) +
+观察错误处的上下代码,只需将`class BookReadSerializer(serializers.ModelSerializer)`的代码挪动到`class CategorySerializer(serializers.ModelSerializer)`后即可修复,先定义CategorySerializer()再使用。 +## 错误② +运行代码后发现API无法正确打开,返回类型错误。 +![__str__ 返回非字符串](img_3.png) +
+仔细观察`model.py`和其余两个API后发现,在中存在空值,不能作为返回。 +![img_4.png](img_4.png) +
+所以可将返回值作修改,不为空返回真实值,为空则返回字符串`null` +![img_6.png](img_6.png) +# 使用Django REST framework实现RESTful API +本次开发中共有四个需求: +```python +1、书本总价 +2、书本总数 +3、最多书作者 +4、最多书种类 +``` +解决思路: +```python +1、新建一个表存储需求数据 +2、利用ORM聚合查询出相应数据 +3、新建API将数据呈现 +``` +## 一、在models.py中新建模型 +```python +class Summary(models.Model): + sumprice = models.DecimalField(max_digits=20, verbose_name='书本总价', decimal_places=2) + sumowned = models.IntegerField(verbose_name='书本总数') + mostauthor = models.ManyToManyField(Author, verbose_name='最多书作者') + mostcategory = models.ManyToManyField(Category, verbose_name='最多书种类') +``` +迁移模型: +```python +python manage.py makemigrations +python manage.py migrate +``` +## 二、在serializers.py中创建序列化器 +```python +class SummarySerializer(serializers.ModelSerializer): + sumprice = serializers.SerializerMethodField() + sumowned = serializers.SerializerMethodField() + mostauthor = serializers.SerializerMethodField() + mostcategory = serializers.SerializerMethodField() + def get_sumprice(self, obj): + my_dict1 = Book.objects.aggregate(sum_price=Sum(F('price') * F('owned'), output_field=DecimalField())) + return my_dict1.get('sum_price') + def get_sumowned(self, obj): + my_dict2 = Book.objects.aggregate(sum_owned=Sum(F('owned'), output_field=DecimalField())) + return my_dict2.get('sum_owned') + def get_mostauthor(self, obj): + my_list1 = Book.objects.values('author').annotate(total=Count('author')) + return Author.objects.get(id=int(my_list1[0]['author'])).name + def get_mostcategory(self, obj): + my_list2 = Book.objects.values('category').annotate(total=Count('category')) + return Category.objects.get(id=int(my_list2[0]['category'])).title + class Meta: + model = Summary + fields = ['sumprice', 'sumowned', 'mostauthor', 'mostcategory'] +``` +## 三、在views.py中创建视图聚合 +```python +class SummaryViewSet(viewsets.ModelViewSet): + queryset = Summary.objects.all() + serializer_class = SummarySerializer +``` +## 四、在urls.py中添加API +```python +router.register(r'summary', SummaryViewSet) +``` +## 五、启动项目 +![img_7.png](img_7.png) \ No newline at end of file