Django模型字段元组值增查及多服务存储的DRF技术咨询
嘿,作为刚接触Django/DRF的新手,你这个问题我当初入门的时候也纠结过!先给你敲个重点:千万别用数组或者逗号分隔值来存这种多服务关联,这完全违背了MySQL这类关系型数据库的设计逻辑,后续查数据、改数据会超级头疼!
正确的打开方式:用多对多(Many-to-Many)关系
一个Booking对应多个Service,一个Service也能被多个Booking选中,这刚好是多对多关系的典型场景,Django和DRF对这种场景支持得非常完善,完全不用自己折腾存储格式。
1. 先搞定Django模型
在你的models.py里这么定义:
from django.db import models class Service(models.Model): sid = models.AutoField(primary_key=True) # 如果你坚持用sid当主键,就这么写;也可以用Django默认的id字段 service_name = models.CharField(max_length=100) # 用choices把服务类型固定成Type1和Type2,比直接存字符串更规范 type = models.CharField(max_length=20, choices=[('Type1', 'Type1'), ('Type2', 'Type2')]) def __str__(self): return self.service_name class Booking(models.Model): id = models.AutoField(primary_key=True) data = models.DateField() # 这里应该是date吧?你写的data可能是笔误~ desc = models.TextField(blank=True, null=True) # 多对多字段,Django会自动生成中间表来维护Booking和Service的关联 services = models.ManyToManyField(Service, related_name='bookings') def __str__(self): return f"Booking {self.id} on {self.data}"
写完之后执行python manage.py makemigrations和python manage.py migrate,Django会自动帮你创建中间表,专门用来存每个Booking关联的Service ID,完全不用你手动处理存储细节。
2. DRF序列化器怎么处理?
接下来在serializers.py里写序列化逻辑,两种常见的写法供你选:
写法一:接收服务ID数组,返回ID数组
适合前端只需要传服务ID,后端也只返回ID的场景:
from rest_framework import serializers from .models import Booking, Service class BookingSerializer(serializers.ModelSerializer): services = serializers.PrimaryKeyRelatedField( many=True, queryset=Service.objects.all() ) class Meta: model = Booking fields = ['id', 'data', 'desc', 'services']
前端请求的时候,只需要传"services": [1,2,3]这样的数组就行,DRF会自动帮你关联对应的服务。
写法二:接收服务ID数组,返回服务详细信息
如果前端需要看到服务的名称、类型等详情,可以这么写:
from rest_framework import serializers from .models import Booking, Service class ServiceSerializer(serializers.ModelSerializer): class Meta: model = Service fields = ['sid', 'service_name', 'type'] class BookingSerializer(serializers.ModelSerializer): # 写一个只用来接收ID的字段(write_only) service_ids = serializers.PrimaryKeyRelatedField( many=True, queryset=Service.objects.all(), write_only=True ) # 再写一个用来返回详细信息的字段(read_only) services = ServiceSerializer(many=True, read_only=True) class Meta: model = Booking fields = ['id', 'data', 'desc', 'service_ids', 'services'] def create(self, validated_data): # 取出service_ids,剩下的用来创建Booking service_ids = validated_data.pop('service_ids') booking = Booking.objects.create(**validated_data) # 关联服务 booking.services.set(service_ids) return booking
这样前端传"service_ids": [1,2,3],后端返回的时候会把对应的服务详情放在services字段里,非常友好。
为什么坚决不推荐数组/逗号分隔值?
我当初也想过偷懒这么干,后来踩了大坑:
- 查询效率极低:如果存的是逗号分隔的字符串比如
"1,2,3",要查包含某个服务的所有Booking,只能用LIKE '%1%',不仅慢,还会把ID是11、12的服务也误查出来。 - 数据没保障:关系型数据库的外键约束能确保你关联的服务一定存在,但存字符串的话,随便写个不存在的ID也能存进去,数据很容易变脏。
- 维护超级麻烦:要给某个Booking加个服务,得先把字符串拆成数组,修改后再拼接回去,稍不留神就会出错。
额外小提示
既然你说每类服务固定10种,可以提前把这些服务数据导入到数据库里,比如写个简单的脚本:
# 可以在Django shell里执行,或者写个management命令 from yourapp.models import Service # 导入Type1的10种服务 for i in range(1, 11): Service.objects.create(service_name=f"Type1 Service {i}", type='Type1') # 导入Type2的10种服务 for i in range(1, 11): Service.objects.create(service_name=f"Type2 Service {i}", type='Type2')
这样用户预订的时候直接选已有的服务ID就行啦~
内容的提问来源于stack exchange,提问作者Vallabh
相关产品推荐
相关产品推荐

