后端的数据到前端(get请求),以json的格式
准备工作,在models中新建表格,并且自己添加2行数据
from django.db import models class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) author = models.CharField(max_length=32, null=True)
第一, 自己写一个序列化器serializer,里面写需要序列化的字段(在项目下新建serializer.py文件)
from rest_framework import serializers class BookSerializer(serializers.Serializer): name = serializers.CharField(max_length=32) price = serializers.CharField() author = serializers.CharField()
第二,在views中,重新重写get方法,继承的是APIView
from rest_framework.views import APIView from .models import Book from rest_framework.response import Response from .serializer import BookSerializer class BookView(APIView): def get(self, request): book_list = Book.objects.all() res = BookSerializer(instance=book_list, many=True) # print(res) return Response(res.data)
第三,开放后台端口,在url中添加路由,路由book是接口路径
from django.contrib import admin from django.urls import path from app01 import views urlpatterns = [ path('admin/', admin.site.urls), path('book/', views.BookView.as_view()), ]
启动项目,访问接口地址,http://127.0.0.1:8000/book/
显示接口数据,用postman访问,结果为
定制字段的2中方法,经过流程参照下图
第一种,在自定义的序列化器中,增加字段,且需要配合一个同名方法使用
from rest_framework import serializers class BookSerializer(serializers.Serializer): name = serializers.CharField(max_length=32) price = serializers.CharField() author = serializers.CharField() # 定制价格字段,必须在views中,写入get_price_info price_info = serializers.SerializerMethodField() def get_price_info(self,obj): return '价格是' + str(obj.price)
第二种,在序列化类里写新方法的同时,在models中写方法,这里的字段就是models的方法名,price_info是什么,就返回什么
serializer中新增
from rest_framework import serializers class BookSerializer(serializers.Serializer): name = serializers.CharField(max_length=32) price = serializers.CharField() author = serializers.CharField() # 下面是新增的字段 price_info = serializers.CharField()
models中新增get_方法
from django.db import models class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) author = models.CharField(max_length=32, null=True) def price_info(self): return '价格是' + str(self.price)
在系列化类中,可以增加各种选项参数,最常用,且需要记住的是
CharField # 输出的是字符串 IntegerField # 输出的是整形 FloatField # 输出的是浮点型 DecimalField # 输出的是浮点型,可以制定小数的位数
DateTimeField # 输出的是时间
内部参数如下
| 字段 | 字段构造方式 | | ----------------------- | ------------------------------------------------------------ | | **BooleanField** | BooleanField() | | **NullBooleanField** | NullBooleanField() | | **CharField** | CharField(max_length=None, min_length=None, allow_blank=False, trim_whitespace=True) | | **EmailField** | EmailField(max_length=None, min_length=None, allow_blank=False) | | **RegexField** | RegexField(regex, max_length=None, min_length=None, allow_blank=False) | | **SlugField** | SlugField(max*length=50, min_length=None, allow_blank=False) 正则字段,验证正则模式 [a-zA-Z0-9*-]+ | | **URLField** | URLField(max_length=200, min_length=None, allow_blank=False) | | **UUIDField** | UUIDField(format=’hex_verbose’) format: 1) `'hex_verbose'` 如`"5ce0e9a5-5ffa-654b-cee0-1238041fb31a"` 2) `'hex'` 如 `"5ce0e9a55ffa654bcee01238041fb31a"` 3)`'int'` - 如: `"123456789012312313134124512351145145114"` 4)`'urn'` 如: `"urn:uuid:5ce0e9a5-5ffa-654b-cee0-1238041fb31a"` | | **IPAddressField** | IPAddressField(protocol=’both’, unpack_ipv4=False, **options) | | **IntegerField** | IntegerField(max_value=None, min_value=None) | | **FloatField** | FloatField(max_value=None, min_value=None) | | **DecimalField** | DecimalField(max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None) max_digits: 最多位数 decimal_palces: 小数点位置 | | **DateTimeField** | DateTimeField(format=api_settings.DATETIME_FORMAT, input_formats=None) | | **DateField** | DateField(format=api_settings.DATE_FORMAT, input_formats=None) | | **TimeField** | TimeField(format=api_settings.TIME_FORMAT, input_formats=None) | | **DurationField** | DurationField() | | **ChoiceField** | ChoiceField(choices) choices与Django的用法相同 | | **MultipleChoiceField** | MultipleChoiceField(choices) | | **FileField** | FileField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) | | **ImageField** | ImageField(max_length=None, allow_empty_file=False, use_url=UPLOADED_FILES_USE_URL) | | **ListField** | ListField(child=, min_length=None, max_length=None) | | **DictField** | DictField(child=) |
**选项参数:** | 参数名称 | 作用 | | ------------------- | ----------------------------- | | **max_length** | 最大长度(CharField) | | **min_lenght** | 最小长度(CharField) | | **allow_blank** | 是否允许为空(CharField) | | **trim_whitespace** | 是否截断空白字符(CharField) | | **max_value** | 最小值 (IntegerField) | | **min_value** | 最大值(IntegerField) |
**通用参数**: | 参数名称 | 说明 | | ------------------ | --------------------------------------------- | | **read_only** | 表明该字段仅用于序列化输出,默认False | | **write_only** | 表明该字段仅用于反序列化输入,默认False | | **required** | 表明该字段在反序列化时必须输入,默认True | | **default** | 反序列化时使用的默认值 | | **allow_null** | 表明该字段是否允许传入None,默认False | | **validators** | 该字段使用的验证器(不太用) | | **error_messages** | 包含错误编号与错误信息的字典 | | **label** | 用于HTML展示API页面时,显示的字段名称 | | **help_text** | 用于HTML展示API页面时,显示的字段帮助提示信息 |
补充is_valid方法
首先我们看看一般验证过程: 1.方法函数is_valid()的作用检查seld.errors是否都正确,例如长度,空值,类型等 2.分析errors.里面判断_errors是都为空,如果为空返回self.full_clean(),否则返回self._errors 3.跳到full_clean(),该函数里面设置_errors和cleaned_data这两个字典,分别是存放存错误字段和正确字段的。 4.在full_clean的最后有是[self._clean_fields()],表示校验字段,(5,6,7是关于_errors和cleaned_data的左右) 5._clean_fields函数开始循环校验每个字段,而真正校验字段的是field.clean(value) 6.在_clean_fields中可以看到,会将字段按结果添加到_errors和cleaned_data这两个字典中 7.结尾部分还设置了钩子,找clean_XX形式的,并且会执行。执行错误信息也会添加到_errors中 至此校验完成~
反序列化,需要用到post请求,来保证数据的安全,后端接收到之后入库
首先,在views的BookView中中新增post方法
def post(self, request): # 首先要把前端传过来的json数据,先接收,再校验,再进行序列化 ser = BookSerializer(data=request.data) # 得到的是对象 print(ser) if ser.is_valid(): ser.save() # 但是这里是我们自己新增的save方法,但是不知道要save到哪张表 # 又因为save的本质,是create方法,所以我们要在序列化类中,重写create方法 return Response(ser.data) return Response({'code': 100, 'msg': '保存成功'})
第二,在serialiser中,重写create方法
class BookSerializer(serializers.Serializer): name = serializers.CharField(max_length=32) price = serializers.CharField() author = serializers.CharField() # 定制价格字段,必须在views中,写入get_price_info # price_info = serializers.SerializerMethodField() # def get_price_info(self,obj): # return '价格是' + str(obj.price) price_info = serializers.CharField(read_only=True) # 这里的参数表示只读,不能新增 def create(self, validated_data): # 指定这里返回的是book表 book = Book.objects.create(**validated_data) return book
此时,从postman中以post新增数据
修改,查询单个,删除一个,需要向不同的地址来发送请求,最好重写一个视图类
首先,需要修改路由
urlpatterns = [ path('admin/', admin.site.urls), path('book/', views.BookView.as_view()), path('book/<int:pk>', views.BookDetailView.as_view()), ]
第二,需要重写update方法
def update(self, instance, validated_data): # validated_data校验过后的数据,instance 是要修改的对象 instance.name = validated_data.get('name') instance.price = validated_data.get('price') instance.author = validated_data.get('author') instance.save() # 这里是模型对象自带的save方法,保存到数据库中 return instance #
第三,在views中,重写视图类,新增put请求
class BookDetailView(APIView): def get(self, request, pk): pass def delete(self, request, pk): pass def put(self, request, pk): # 修改什么数据 book = Book.objects.filter(pk=pk).first() # 既有instance,又有data,表示修改 ser = BookSerializer(instance=book, data=request.data) if ser.is_valid(): ser.save() # 调用内部方法的save return Response(ser.data) # 修改成功返回的数据 return Response({'mag':'修改错了', 'err': ser.errors})
需要在update中来进行判断,null=None,序列化类中,增加require参数=True,表示非必填
def get(self, request, pk): book = Book.objects.filter(pk=pk).first() ser = BookSerializer(book) return Response(ser.data)
def delete(self, request, pk): res = Book.objects.filter(pk=pk).delete() print(res) # 注意,这里res返回的是一个元祖,前面1个数值表示影响的行数,(1, {'app01.Book': 1}) if res[0] == 0: return Response({'msg': '数据不存在'}) return Response({'msg': '删除成功'})
校验,就是max_lenth,min_lenth等,但是我觉得不够,所以可以自定义校验钩子,写在自定义的序列化类中写,之前需要先导入异常模块
from rest_framework.exceptions import ValidationError def validate_name(self, attrs): # 这里的name,需要和序列化类中的字段名相对应 # attrs就是前端传过来的数据,然后再这里进行校验 if attrs.startswith('sb'): # 名字不能以sb开头 raise ValidationError('名字不能用sb开头') # 导入模块之后,抛出异常 else: return attrs
执行顺序:先走字段自己规则,再走局部钩子,再走全局钩子
def validate(self, attrs): if attrs.get('name') == attrs.get('author'): raise ValidationError('书名不能等于作者名') else: return attrs
查询所有,查询单挑,删除,都与上面相同
但是 新增 与 修改 有一点点差别,并且自定义字段的话,需要在fields中注册
class BookSerializer2(serializers.ModelSerializer): class Meta: model = Book # 表示跟哪个表有关系 # fields = '__all__' # 因为是在修改源码,所以变量的单词千万不能拼错 # 如果要自定义字段,也要在这里进行注册 fields = ['name', 'price', 'author', 'price_info'] # 列表中的字段,会在前端显示 price_info = serializers.SerializerMethodField() def get_price_info(self, obj): return '价格是'+ str(obj.price)
class BookSerializer2(serializers.ModelSerializer): class Meta: model = Book # 表示跟哪个表有关系 # fields = '__all__' # 因为是在修改源码,所以变量的单词千万不能拼错 # 如果要自定义字段,也要在这里进行注册 fields = ['name', 'price', 'author', 'price_info'] # 列表中的字段,会在前端显示 extra_kwargs = { 'name': {'write_only': True, 'max_length': 8, 'min_length': 3} # 相当于和之前括号里面加的限制条件一样 } price_info = serializers.SerializerMethodField() def get_price_info(self, obj): return '价格是' + str(obj.price)