You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 08:25:24