Django Many-to-Many多对多关联GET/POST请求使用及set()方法疑问
核心结论
你需要使用set()或者add()这类Django ORM提供的多对多操作方法,且必须在两个关联模型的实例都已经保存到数据库之后才能调用,否则会触发操作报错。
现有代码的核心问题
- 你调用了不存在的
new_quote.set()方法,正确的调用方式是操作多对多字段本身:new_quote.tags.set() - 第三方API返回的tags是字符串列表,你直接把列表赋值给Tag模型的CharField类型
tag字段属于类型错误,每个标签字符串需要对应独立的Tag实例 - 多对多关联建立的前提是两端的实例都已经完成数据库保存,你创建Quote和Tag实例后没有调用
save()就尝试关联,操作无效 - 你的TagSerializer没有指定序列化字段,运行时会触发DRF报错
多对多常用操作说明
add(*objs):给当前实例追加关联对象,不会清空已有的关联关系set(objs_list):用传入的对象列表覆盖当前所有关联关系,等价于先清空原有关联再批量添加新关联remove(*objs):移除指定的关联对象clear():清空当前实例的所有关联关系
代码修改示例
1. 修正serializers.py
from rest_framework import serializers from .models import Quote, Tag class TagSerializer(serializers.ModelSerializer): class Meta: model = Tag # 必须明确指定序列化字段 fields = ['id', 'tag'] class QuoteSerializer(serializers.ModelSerializer): tags = TagSerializer(read_only = True, many=True) class Meta: model = Quote fields = ['id', 'frase', 'autor', 'tags']
2. 修正views.py的api_quotes函数
@api_view(['GET', 'POST']) def api_quotes(request): if request.method == 'POST': new_quote_data = request.data # 先创建并保存Quote实例 quote = Quote.objects.create( id=new_quote_data['id'], frase=new_quote_data['frase'], autor=new_quote_data['autor'] ) # 处理传入的标签列表,去重创建/查询Tag实例 tag_objs = [] for tag_name in new_quote_data['tags']: tag, _ = Tag.objects.get_or_create(tag=tag_name) tag_objs.append(tag) # 调用多对多字段的set方法完成关联 quote.tags.set(tag_objs) # 第三方API请求逻辑保持不变 url = "https://quotes15.p.rapidapi.com/quotes/random/" querystring = {"language_code":"pt"} headers = { 'x-rapidapi-host': "quotes15.p.rapidapi.com", 'x-rapidapi-key': "4a3bce6d8cmsh044349ed231bf95p15f17ajsn846aa4e94d98" } r = requests.get(url, querystring, headers=headers) quote_json = r.json() id = quote_json['id'] frase = quote_json['content'] autor = quote_json['originator']['name'] tags = quote_json['tags'] # 先创建并保存Quote实例 new_quote = Quote.objects.create( id=id, frase=frase, autor=autor ) # 处理标签列表 tag_objs = [] for tag_name in tags: tag, _ = Tag.objects.get_or_create(tag=tag_name) tag_objs.append(tag) # 此处调用set方法完成多对多关联 new_quote.tags.set(tag_objs) serialized_quote = QuoteSerializer(new_quote) return Response(serialized_quote.data)
内容的提问来源于stack exchange,提问作者Henrique Martinelli Frezzatti
相关产品推荐
相关产品推荐

