drf manytomany though

drf manytomany though

models.py

假设有分组表和套餐表, 规划和套餐是多对多关系:即一个规划可以包含多个套餐,一个套餐可以属于多个分组;
典型的django manytomany关系, 通过through关联,以便存更多信息(比如数量)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class PlanGroup(models.Model):
"""
规划分组
"""
name = models.CharField(max_length=100, verbose_name="分组名称", unique=True)
product = models.ManyToManyField(Product, verbose_name=_('套餐'), through="PlanGroupProductRel")

class Meta:
verbose_name = "分组"
verbose_name_plural = "分组列表"


class Product(models.Model):
"""主机套餐(Product)"""
vendor = models.ForeignKey(Vendor, on_delete=models.PROTECT, verbose_name=_('供应商'))
name = models.CharField(max_length=64, default='', blank=True, verbose_name=_('名称'))
label = models.CharField(max_length=64, unique=True, verbose_name=_('标签')

class Meta:
verbose_name = _('SKU: 主机套餐')
verbose_name_plural = _('SKU: 主机套餐')

def __str__(self):
return self.label


class PlanGroupProductRel(models.Model):
"""
分组套餐多对多
"""
product = models.ForeignKey(Product, on_delete=models.CASCADE, verbose_name=_("套餐"), db_constraint=False)
plan_group = models.ForeignKey(PlanGroup, on_delete=models.CASCADE, verbose_name=_("分组"), db_constraint=False)
server_count = models.IntegerField(default=15)

class Meta:
verbose_name = "分组套餐"
verbose_name_plural = "分组套餐列表"

serializers.py

关键是serializers这里,有两点要注意

  1. 要重写creat,update方法,存储关联数据
  2. 关联的serializer类需要指定source=plangroupproductrel_set
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class PlanGroupProductRelSerializer(serializers.ModelSerializer):
class Meta:
model = PlanGroupProductRel
fields = ("product", "server_count",)

class PlanGroupWriteSerializer(serializers.ModelSerializer):
switch_info = serializers.JSONField()
products = PlanGroupProductRelSerializer(source="plangroupproductrel_set", many=True)

class Meta:
model = PlanGroup
fields = (
"id", "products", "name", "rack_count", "switch_info", "aoc_count",)

@transaction.atomic
def create(self, validated_data):
products = validated_data.pop("plangroupproductrel_set")
plan_group = PlanGroup.objects.create(**validated_data)
for product in products:
PlanGroupProductRel.objects.create(product=product['product'], plan_group=plan_group,
server_count=product["server_count"])
return plan_group

@transaction.atomic
def update(self, instance, validated_data):
products = validated_data.pop("plangroupproductrel_set")
for item in validated_data:
if PlanGroup._meta.get_field(item):
setattr(instance, item, validated_data[item])
PlanGroupProductRel.objects.filter(plan_group=instance).delete()
for product in products:
PlanGroupProductRel.objects.create(product=product['product'], plan_group=instance,
server_count=product["server_count"])
instance.save()
return instance

views.py

view层就比较简单了, 执行用viewsets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class PlanGroupViewSet(DynamicTableFieldMixin, viewsets.ModelViewSet):
"""
A viewset for viewing and editing user ProductColor.
"""
queryset = PlanGroup.objects.all()
serializer_class = PlanGroupReadSerializer

def get_serializer_class(self, *args, **kwargs):
if self.action in ["list", "retrieve"]:
return PlanGroupReadSerializer
return PlanGroupWriteSerializer

def perform_create(self, serializer):
serializer.save(creator=self.request.user)

post数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
{
"name": "test_group_2222",
"products":[
{
"product": 26,
"server_count": 10
},
{
"product": 28,
"server_count": 15
}
],
"rack_count": 5,
"switch_info": {
"1": [
{
"name": "sw1",
"count": 20,
"is_main": true
},
{
"name": "sw2",
"count": 11,
"is_main": true
}
],
"2": [
{
"name": "sw1",
"count": 30,
"is_main": false
}
]
},
"aoc_count": 2
}