rest_framwork框架的简单使用
from django.http.response import JsonResponse class BookListView(View): def get(self, request, *args, **kwargs): # 返回的 是json格式 all_books = models.Book.objects.all().values('id', 'title', 'pub_date') return JsonResponse(all_books)
错误:
In order to allow non-dict objects to be serialized set the safe parameter to False.
为了一个非字典的对象可以被序列化需要设置 safe=False
return JsonResponse(all_books,safe=False)
错误:
Object of type 'QuerySet' is not JSON serializable
all_books是QuerSet类型
return JsonResponse(list(all_books),safe=False)
得到的文字是ascii类型
return JsonResponse(list(all_books), safe=False, json_dumps_params={'ensure_ascii': False})
django中的序列化器
from django.core import serializers class BookListView(View): def get(self, request, *args, **kwargs): all_books = models.Book.objects.all() data = serializers.serialize('json', all_books) return HttpResponse(data)
得到的结果
灵活度不高
django的rest_framwork框架 做序列化
get请求
需要有djangorestframework包
from rest_framework.views import APIView from rest_framework.response import Response class BookListView(APIView): def get(self, request, *args, **kwargs): all_books = models.Book.objects.all() return Response(all_books)
错误
rest_framework/api.html
没有注册 在settings中注册app
'rest_framework'
错误
Object of type 'Book' is not JSON serializable
不能被序列化
all_books = models.Book.objects.all().values()
得到页面 里面的内容就是book表中的数据 还需要得到 表中关联的数据
需要序列化器 去序列化出想得到的结果
现在的问题是 一本书有多个作者 拿到的all_books的 是一本书对应一个作者 多个作者就有多条数据 这种情况是 默认序列化导致的
现在需要自己 创建序列化器 然后序列化之后的值再传给前端
创建一个序列化文件 serializer并在里面写 需要与models中'大体一样'
from rest_framework import serializers class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.DecimalField(max_digits=6, decimal_places=2) pub_data = serializers.DateTimeField()
在views中
from app01.serializer import BookSerializers class BookListView(APIView): def get(self, request, *args, **kwargs): all_books = models.Book.objects.all() ser_obj = BookSerializers(all_books) #ser_obj是对象 return Rseponse(ser_obj.data) #ser_obj是序列化的结果
报错
'QuerySet' object has no attribute 'title'.
queryset对象没有title的属性
因为all_books中是多个对象
ser_obj = BookSerializers(all_books, many=True)
或
all_books = models.Book.objects.all().first() #这样就值拿到了第一个 只序列化一个对象
这样就拿到了这些普通的字段
然后 在serializer中写 pub是外键关联的是出版社 author是多对多 关联作者表
pub= serializers.IntegerField()
报错
int() argument must be a string, a bytes-like object or a number, not 'Publisher'
pub_id = serializers.IntegerField()
这样的到的还是出版社的id
class PublisherSerializers(serializers.Serializer): name = serializers.CharField() class BookSerializers(serializers.Serializer): title = serializers.CharField() price = serializers.DecimalField(max_digits=6, decimal_places=2) pub_date = serializers.DateTimeField() pub = PublisherSerializers()
这样就得到了
{ "title": "没事就念经", "price": "999.99", "pub_date": "2019-01-01T10:10:10Z", "pub": { "name": "冰棍" } },
再写authors
authors = serializers.SerializerMethodField()
报错
'BookSerializers' object has no attribute 'get_authors'
pub = PublisherSerializers() authors = serializers.SerializerMethodField() def get_authors(self, obj): print(obj)
authors是 null 但是print(obj) 是Book object
pub = PublisherSerializers() authors = serializers.SerializerMethodField() #.Ser之后就去调用get_字段名的方法 def get_authors(self, obj): #obj是当前循环book的对象 ser_obj = AuthorSerializers(obj.authors.all(),many=True) #进行序列化 return ser_obj.data
post请求
def post(self, request, *args, **kwargs): print(request.data)
注意的是 在添加时 需要将外键和多对多 添加的数据变为id
request.data得到的就是 提交的所有护数据
def post(self, request, *args, **kwargs): ser_obj = BookSerializers(data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) else: return Response(ser_obj.errors)
报错
HTTP 200 OK Allow: GET, POST, HEAD, OPTIONS Content-Type: application/json Vary: Accept { "pub": { "non_field_errors": [ "Invalid data. Expected a dictionary, but got int." ] } }
这个错误是因为 添加的时的数据是int型 但是pub不是int类型字段
所以需要在serializer的Book中添加
post_pub = serializers.IntegerField() #写入时 post post_authors = serializers.ListField()
报错
'Book' object has no attribute 'post_pub'.
在发出get请求时 没有post_pub字段
这时需要在字段里填写
read_only=True #在读的时候 get
write_only=True #在写入的时候 post
pub = PublisherSerializers(read_only=True) authors = serializers.SerializerMethodField(read_only=True) #读的时候 get post_pub = serializers.IntegerField(write_only=True) #写入时 post post_authors = serializers.ListField(write_only=True)
再继续添加
报错
`create()` must be implemented.
需要执行'create'方法
def create(self, validated_data): #validated_data 是传过来的所有数据 book_obj = models.Book.objects.create( title=validated_data['title'], price=validated_data['price'], pub_date=validated_data['pub_date'], pub_id=validated_data['post_pub'], ) book_obj.authors.set(validated_data['post_authors']) return book_obj
put请求
先设计urls地址 因为需要知道是需要修改的具体id 需要再单独写一个视图
class BookView(APIView): def get(self, request, pk, *args, **kwargs): book_obj = models.Book.objects.filter(pk=pk).first() ser_obj = BookSerializers(book_obj) return Response(ser_obj.data)
这是拿到的一个对象 然后
def put(self, request, pk, *args, **kwargs): book_obj = models.Book.objects.filter(pk=pk).first() ser_obj = BookSerializers(instance=book_obj,data=request.data) if ser_obj.is_valid(): ser_obj.save() return Response(ser_obj.data) return Response(ser_obj.errors)
报错
`update()` must be implemented.
需要'update'这个方法来更新
在serializer中写
def update(self, instance, validated_data): #instance 是要修改的数据 #validated_data是提交的数据 instance.title = validated_data['title'] instance.price = validated_data['price'] instance.pub_date = validated_data['pub_date'] instance.pub_id = validated_data['post_pub'] instance.save() instance.authors.set(validated_data['post_authors']) return instance
这样是只能修改所有数据
我想要的是提交我想要修改的数据就没办法改了
允许部分进行修改
ser_obj = BookSerializers(instance=book_obj, data=request.data, partial=True)
还是报错
这就可以用get取 取不到 就添加一个默认值
def update(self, instance, validated_data): # instance 是要修改的数据 validated_data是提交的数据 instance.title = validated_data.get('title', instance.title) instance.price = validated_data.get('price', instance.price) instance.pub_date = validated_data.get('pub_date', instance.pub_date) instance.pub_id = validated_data.get('post_pub', instance.pub_id) instance.save() instance.authors.set(validated_data.get('post_authors', instance.authors.all())) return instance
然后修改数据
{
"title": "没事就念念经77777",
}
报错
"detail": "JSON parse error - Expecting property name enclosed in double quotes: line 3 column 1 (char 30)"
原因就是因为 修改的数据后面有一个逗号
delete请求
def delete(self, request, pk, *args, **kwargs): obj = models.Book.objects.filter(pk=pk).first() if obj: obj.delete() return Response({'smg': '删除成功'}) return Response({'error': '删除的数据不存在'})
在BookListView中有 get post
在BookView中有 get put delete
较简洁的使用方法
在写serializer时的serializers.Serializer, 感觉就和 forms.form比较相似
那有forms.ModelForm也就是有serializers.ModelSerializer
对上面进行补充 is_valid()是对数据进行校验
在这里可以自己来设置校验的约束
def validate_title(value): if 'li' in value: raise serializers.ValidationError
然后在title放约束条件 也就是上面我自己定义的这个
title = serializers.CharField(validators=[validate_title])
这样就可以了
修改数据
{ "title": "没事li就念经" }
这样就报错了 因为在修改的数据中有li 当.is_valid()的时候就会调用validate_title方法 发现里面有li就抛出错误.
HTTP 200 OK Allow: GET, PUT, DELETE, HEAD, OPTIONS Content-Type: application/json Vary: Accept { "title": [ "Invalid input." ] }
这就是一个简单的
局部钩子
用上局部钩子会 更灵活
在class中定义局部钩子 title是字段名
def validate_title(self, value): if 'li' in value: raise serializers.ValidationError
修改数据 title中有li
报错
NOT NULL constraint failed: app01_book.title
为空
def validate_title(self, value): if 'li' in value: raise serializers.ValidationError return value
给钩子一个返回值就可以正常使用了
全局钩子
def validate(self, attrs): return attrs
接下里就是ModelSerializer
ModelSerializer的基础使用
class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = '__all__'
这样就 直接拿到了 Book表中的 所有信息 但是外键和多对多的字段 拿到的都是id
class BookSerializer(serializers.ModelSerializer): class Meta: model = models.Book fields = '__all__' depth = 1
depth是深度的意思 这样就拿到了 所有的数据
虽然拿到了 但是也只是能在 显示中方便使用 在提交数据时是没办法使用的
所以这种方法不好用 去掉这个这个方法 还是拿到的是id id就是book表中数据库的数据
可以把这些特殊的字段 重写一下
class BookSerializer(serializers.ModelSerializer): pub_info = PublisherSerializers() #重写pub字段 class Meta: model = models.Book fields = '__all__' # depth = 1
报错
'Book' object has no attribute 'pub_info'.
是因为没有pub_info的这个字段 之前重写字段的时候可以使用是因为在 重写的字段后面加上了write_only=True
class BookSerializer(serializers.ModelSerializer): pub_info = serializers.SerializerMethodField() #重写pub字段 def get_pub_info(self,obj): return PublisherSerializers(obj.pub).data class Meta: model = models.Book fields = '__all__' # depth = 1
这样就可以了
在继续重写 多对多字段
class BookSerializer(serializers.ModelSerializer): pub_info = serializers.SerializerMethodField() # 重写pub字段 authors_info = serializers.SerializerMethodField() def get_pub_info(self, obj): return PublisherSerializers(obj.pub).data def get_authors_info(self, obj): return AuthorSerializers(obj.authors.all(), many=True).data class Meta: model = models.Book fields = '__all__' # depth = 1
这样就可以了 但是显示的时候 pub和authors字段还显示 pub_info 和authors_info应该在read是使用 pub和authors应该在write时使用
class BookSerializer(serializers.ModelSerializer): pub_info = serializers.SerializerMethodField(read_only=True) # 重写pub字段 authors_info = serializers.SerializerMethodField(read_only=True) def get_pub_info(self, obj): return PublisherSerializers(obj.pub).data def get_authors_info(self, obj): return AuthorSerializers(obj.authors.all(), many=True).data class Meta: model = models.Book fields = '__all__' # depth = 1 extra_kwargs = { 'pub': {'write_only': True}, 'authors': {'write_only': True}, }
这样就可以了 比较上一种方法 这种相对简单一些 不用再写那么多方法了
但是最后要注意的是 与之前相比在添加的时候 post_pub就应该换成pub了