Django改ForeignKey为ManyToManyField时报ManyRelatedManager无slug属性解决方案
这个需求完全可以实现,报错原因是ManyToManyField返回的是关联对象管理器,而非外键对应的单个模型实例,原有直接访问suites.slug的逻辑不再适用,按以下步骤调整即可:
步骤1:调整模型定义
首先修改PortfolioUpdate的suites字段为多对多类型,同步修改get_absolute_url方法适配多关联的场景:
class Suite(models.Model): slug = models.SlugField() client_name = models.ForeignKey(Client, blank=True, null=True, on_delete=models.CASCADE) title = models.CharField(max_length=120) def __str__(self): return self.title def get_absolute_url(self): return reverse('courses:detail', kwargs={'slug': self.slug}) @property def lessons(self): # 多对多反向查询逻辑不变,指定排序规则即可 return self.portfolioupdate_set.all().order_by('position') class PortfolioUpdate(models.Model): slug = models.SlugField() title = models.CharField(max_length=120) # 替换原有外键为多对多字段 suite = models.ManyToManyField(Suite) # 如果需要同一个PortfolioUpdate在不同Suite下有独立排序,需要新增中间表存储position字段,示例如下: # suite = models.ManyToManyField(Suite, through='SuiteLessonRelation') position = models.IntegerField() video_url = models.CharField(max_length=200) thumbnail = models.ImageField() def __str__(self): return self.title # 修改方法,接收当前上下文的Suite对象生成对应URL def get_absolute_url(self, current_suite): return reverse('courses:lesson-detail', kwargs={ 'course_slug': current_suite.slug, 'lesson_slug': self.slug }) # 如需独立排序,新增中间表模型 # class SuiteLessonRelation(models.Model): # suite = models.ForeignKey(Suite, on_delete=models.CASCADE) # lesson = models.ForeignKey(PortfolioUpdate, on_delete=models.CASCADE) # position = models.IntegerField() # # class Meta: # ordering = ['position']
步骤2:调整模板调用逻辑
原有模板中调用{{ lesson.get_absolute_url }}的位置,传入视图传过来的当前Suite对象即可:
<!-- 上下文的object就是视图中传入的suite实例 --> <a href="{{ lesson.get_absolute_url(object) }}">{{ lesson.title }}</a>
步骤3:执行数据库迁移
修改模型后执行以下命令完成字段类型切换:
python manage.py makemigrations python manage.py migrate
注意:如果原外键字段已有存量数据,迁移时Django会提示处理原有关联关系,按照提示操作完成数据迁移即可。
内容的提问来源于stack exchange,提问作者Daniel Leach
相关产品推荐
相关产品推荐

