如何设计Django的Booking模型requester字段,使其指向Artist或Venue?
解决方案:使用Django通用外键(Generic Foreign Key)
这是最适配你场景的方案,既能直接关联发起请求的具体对象(Artist或Venue),又不会产生数据库冗余。
实现步骤
先导入Django内置的通用关系模块,再修改Booking模型:
from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation # 假设你的Artist和Venue基础模型(已有则忽略) class Artist(models.Model): name = models.CharField(max_length=100) # 反向关联:从Artist查询自己发起的所有预订 initiated_bookings = GenericRelation('Booking', content_type_field='requester_type', object_id_field='requester_id') class Venue(models.Model): name = models.CharField(max_length=100) # 反向关联:从Venue查询自己发起的所有预订 initiated_bookings = GenericRelation('Booking', content_type_field='requester_type', object_id_field='requester_id') class Booking(models.Model): name = models.CharField(max_length=100) # 原有外键关系 artist = models.ForeignKey(Artist, related_name="events", on_delete=models.CASCADE) venue = models.ForeignKey(Venue, related_name="events", on_delete=models.CASCADE) # 通用外键核心字段 requester_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) requester_id = models.PositiveIntegerField() requester = GenericForeignKey('requester_type', 'requester_id')
代码说明
requester_type:存储关联对象的模型类型(比如Artist或Venue),对应Django内置的ContentType表。requester_id:存储关联对象的主键ID。requester:通过上述两个字段组合,直接指向具体的Artist或Venue对象,使用逻辑和普通外键完全一致。GenericRelation:添加在Artist和Venue中,方便反向查询(例如some_artist.initiated_bookings.all()可直接获取该艺术家发起的所有预订)。
使用示例
# 创建由Artist发起的预订 artist = Artist.objects.get(id=1) venue = Venue.objects.get(id=1) booking = Booking.objects.create( name="夏季巡演预订", artist=artist, venue=venue, requester=artist ) # 创建由Venue发起的预订 booking2 = Booking.objects.create( name="专场演出邀约", artist=artist, venue=venue, requester=venue ) # 判断发起者类型并获取对象 if isinstance(booking.requester, Artist): print(f"发起者:艺术家{booking.requester.name}") elif isinstance(booking.requester, Venue): print(f"发起者:场地{booking.requester.name}")
替代方案(若排斥通用外键)
如果觉得通用外键复杂度高,也可以用双外键加校验的方式,但会存在数据库冗余:
from django.core.exceptions import ValidationError class Booking(models.Model): name = models.CharField(max_length=100) artist = models.ForeignKey(Artist, related_name="events", on_delete=models.CASCADE) venue = models.ForeignKey(Venue, related_name="events", on_delete=models.CASCADE) requester_artist = models.ForeignKey(Artist, null=True, blank=True, on_delete=models.SET_NULL, related_name="initiated_bookings") requester_venue = models.ForeignKey(Venue, null=True, blank=True, on_delete=models.SET_NULL, related_name="initiated_bookings") # 校验:确保仅设置一个发起者 def clean(self): super().clean() if self.requester_artist and self.requester_venue: raise ValidationError("只能指定一个预订发起者") if not self.requester_artist and not self.requester_venue: raise ValidationError("必须指定一个预订发起者")
这种方案需要手动判断哪个外键有值,查询逻辑更繁琐,不如通用外键优雅。
内容的提问来源于stack exchange,提问作者paul
相关产品推荐
相关产品推荐

