Django through自定义中间表
怎么会这样! 人气:0多对多中间表详解
我们都知道对于ManyToMany字段,Django采用的是第三张中间表的方式。通过这第三张表,来关联ManyToMany的双方。下面我们根据一个具体的例子,详细解说中间表的使用。
默认中间表
class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person) def __str__(self): return self.name
在Group模型中,通过members字段,以ManyToMany方式与Person模型建立了关系。
让我们来看看,中间表是个什么样子的:
首先有一列id,这是Django默认添加的,没什么好说的。然后是Group和Person的id列,这是默认情况下,Django关联两张表的方式。如果你要设置关联的列,可以使用to_field参数。
可见在中间表中,并不是将两张表的数据都保存在一起,而是通过id的关联进行映射。
通过through自定义中间表
一般情况,普通的多对多已经够用,无需自己创建第三张关系表。但是某些情况可能更复杂一点,比如如果你想保存某个人加入某个分组的时间呢?想保存进组的原因呢?
Django提供了一个through参数,用于指定中间模型,你可以将类似进组时间,邀请原因等其他字段放在这个中间模型内。例子如下:
modle:
from django.db import models class Person(models.Model): name = models.CharField(max_length=128) def __str__(self): return self.name class Group(models.Model): name = models.CharField(max_length=128) members = models.ManyToManyField(Person, through='Membership') def __str__(self): return self.name class Membership(models.Model): person = models.ForeignKey(Person, on_delete=models.CASCADE) group = models.ForeignKey(Group, on_delete=models.CASCADE) date_joined = models.DateField() # 进组时间 invite_reason = models.CharField(max_length=64) # 邀请原因
view:
class PersonViews(ModelViewSet): queryset = Person.objects.filter() serializer_class = PersonSerializers class GroupViews(ModelViewSet): queryset = Group.objects.filter() serializer_class = GroupSerializers class MembershipViews(ModelViewSet): queryset = Membership.objects.filter() serializer_class = MembershipSerializers
serializer:
from .models import Person, Group, Membership class MembershipSerializers(serializers.ModelSerializer): class Meta: model = Membership fields = '__all__' class PersonSerializers(serializers.ModelSerializer): class Meta: model = Person fields = '__all__' class GroupSerializers(serializers.ModelSerializer): def to_representation(self, instance): representation = super(GroupSerializers, self).to_representation(instance) representation['members'] = [] for i in PersonSerializers(instance.members, many=True).data: reason = MembershipSerializers(instance.membership_set.get(group=instance.id, person=i['id'])).data['invite_reason'] i['invite_reason'] = reason representation['members'].append(i) return representation class Meta: model = Group fields = '__all__'
从Membership角度,他是建立量到两个模型(Group,Person)的多对1关系,Django在启动时,会自动在其关联的模型上建立"[model]_set"的属性,就想常规的多对一关系一样——实际上他就是常规的多对一关系,只不过Person让其充当另外的角色罢了。
reason = MembershipSerializers(instance.membership_set.get(group=instance.id, person=i[‘id’])).data[‘invite_reason’]
instance.membership_set.get(group=instance.id, person=i[‘id’]) group和person联合查出邀请原因
person和group模型上membership对象的默认名称都将为membership_set.所以通过instance.membership_set.get()可以查看group下的所有关系
person下的所有membership:
# def to_representation(self, instance): # representation = super(PersonSerializers, self).to_representation(instance) # representation['reason'] = MembershipSerializers(instance.membership_set, many=True).data # return representation
加载全部内容