我正在创建课程计划工具。每个课程涵盖多个教学大纲点,并具有相关的资源。保存课程时,我要确保将课程的课程提要点也保存到资源中。
我尝试同时使用signals
和重写save方法来使这项工作有效,但始终得到奇怪的结果。
models.py
class Lesson(models.Model):
lessonslot = models.ForeignKey(TimetabledLesson, on_delete=models.CASCADE)
classgroup = models.ForeignKey(ClassGroup, null=True, blank=False, on_delete=models.SET_NULL)
status = models.CharField(max_length=20, null=True, blank=True)
syllabus_points_covered = models.ManyToManyField(SyllabusPoint, blank=True)
lesson_title = models.CharField(max_length=200, null=True, blank=True)
description = models.TextField(null=True, blank=True)
requirements = models.TextField(null=True, blank=True)
sequence = models.IntegerField(null=False, blank=True)
date = models.DateField(null=True, blank=True)
syllabus = models.ForeignKey(Syllabus, blank=True, null=True, on_delete=models.SET_NULL)
class Meta:
unique_together = ( ("lessonslot", "date"),
("classgroup", "sequence"))
class LessonResources(models.Model):
lesson = models.ForeignKey(Lesson, blank=True, null=True, on_delete=models.SET_NULL)
resource_type = models.CharField(max_length=100, choices=RESOURCE_TYPES, null=False, blank=False)
resource_name = models.CharField(max_length=100, null=True, blank=False)
link = models.URLField(blank=True, null=True)
students_can_view_before = models.BooleanField()
students_can_view_after = models.BooleanField()
available_to_all_classgroups = models.BooleanField()
syllabus_points = models.ManyToManyField(SyllabusPoint, blank=True)
def set_syllabus_points(self):
if self.lesson:
points = self.lesson.syllabus_points_covered.all().order_by('pk')
for point in points:
self.syllabus_points.add(point)
print ('In set_syllabus_points')
print(self.syllabus_points.all())
return self
signals.py
from timetable.models import LessonResources, Lesson
from django.db.models.signals import post_save, m2m_changed
from django.dispatch import receiver
@receiver(m2m_changed, sender=Lesson.syllabus_points_covered.through)
def post_update_lesson_syllabus_pts(sender, instance, **kwargs):
""" After adding a syllabus point to a lesson, update its resources"""
resources = instance.resources()
for resource in resources:
resource.set_syllabus_points()
@receiver(post_save, sender=LessonResources)
def update_resources(sender, instance, **kwargs):
""" After adding a resource, check its syllabus points match its lesson """
print('Before set_syllabus_points')
print(instance.pk)
print(instance.syllabus_points.all())
instance.set_syllabus_points()
print('After set_syllabus_points')
print(instance.syllabus_points.all())
控制台输出
Before set_syllabus_points
105
<QuerySet []>
After set_syllabus_points
<QuerySet [<SyllabusPoint: Motion and Measurement 1.1.1 Use and describe the use of rules and measuring cylinders to find a length or a volume>, <SyllabusPoint: Motion and Measurement 1.1.3 Obtain an average value for a small distance and for a short interval of time by measuring multiples (including the period of a pendulum)>, <SyllabusPoint: Motion and Measurement 1.1.3 Understand that a micrometer screw gauge is used to measure very small distances>, <SyllabusPoint: Motion and Measurement 1.2.1 Define speed>, <SyllabusPoint: Motion and Measurement 1.2.2 Calculate Average speed from total distance / total time>, <SyllabusPoint: Forces 1.3.1 Explain that In the absence of an unbalanced force, an object will either remain at rest or travel with a constant speed in a straight line. Unbalanced forces change motion.>]>
第一个(post_update_lesson_syllabus_pts
)工作正常,但是在创建资源后将点添加到资源中是不可行的。
在调试器中,我可以看到在创建或修改新资源后正在调用resource.set_syllabus_points()
。我还可以看到self.syllabus_points.add(point)
传递了一个有效的教学大纲点point
。但是,当我随后检查资源时(例如,在完成所有内容之后的print(resources.syllabus_points.all())
时,我得到一个空的查询集!我正在管理界面中创建资源(使用附在课程中的内联表单集),而没有选择教学大纲点。
函数运行时,set_syllabus_points
中的print语句输出正确的查询集-为什么它们不进入数据库?
在此先感谢您提供的所有帮助,我敢肯定我错过了一些愚蠢的事情。
更新1
好的,所以我认为我已经将其范围缩小到了管理界面中正在发生的事情。
我添加了上面显示的一些print
语句以查看发生了什么。
这是运行交互式shell的输出:
>>>> resource = LessonResources.objects.get(pk=115)
>>>> resource.syllabus_points.all()
<QuerySet []>
>>>> resource.set_syllabus_points()
In set_syllabus_points
<QuerySet [<SyllabusPoint: Motion and Measurement 1.1.1 Use and describe the use of rules and measuring cylinders to find a length or a volume>, <SyllabusPoint: Motion and Measurement 1.1.3 Obtain an average value for a small distance and for a short interval of time by measuring multiples (including the period of a pendulum)>, <SyllabusPoint: Motion and Measurement 1.1.3 Understand that a micrometer screw gauge is used to measure very small distances>, <SyllabusPoint: Motion and Measurement 1.2.1 Define speed>, <SyllabusPoint: Motion and Measurement 1.2.2 Calculate Average speed from total distance / total time>, <SyllabusPoint: Forces 1.3.1 Explain that In the absence of an unbalanced force, an object will either remain at rest or travel with a constant speed in a straight line. Unbalanced forces change motion.>]>
>>>> resource.syllabus_points.all()
<QuerySet [<SyllabusPoint: Motion and Measurement 1.1.1 Use and describe the use of rules and measuring cylinders to find a length or a volume>, <SyllabusPoint: Motion and Measurement 1.1.3 Obtain an average value for a small distance and for a short interval of time by measuring multiples (including the period of a pendulum)>, <SyllabusPoint: Motion and Measurement 1.1.3 Understand that a micrometer screw gauge is used to measure very small distances>, <SyllabusPoint: Motion and Measurement 1.2.1 Define speed>, <SyllabusPoint: Motion and Measurement 1.2.2 Calculate Average speed from total distance / total time>, <SyllabusPoint: Forces 1.3.1 Explain that In the absence of an unbalanced force, an object will either remain at rest or travel with a constant speed in a straight line. Unbalanced forces change motion.>]>
由于创建或更新资源,set_syllabus_points()
调用signals
时,我们似乎也看到了正确的输出。
但是,即使我们的控制台日志显示它已添加了这些要点,但在加载管理界面时,它们似乎在某些时候又重新设置为空的查询集。可能是由于管理视图/表单中的某些内容缓存多对多关系并重新设置然后返回其初始值引起的吗?
更新2
怀疑,这绝对是modelAdmin的事情。 This StackOverflow post和this blog post解释了正在发生的事情-现在,我只需要解决如何覆盖tabularInLine表单的默认行为...
答案 0 :(得分:0)
一旦您致电post_update_lesson_syllabus_pts
,请在您的resource.save()
中添加语句set_syllabus_points()
。发布您应该能够看到您的资源的课程提要点。
答案 1 :(得分:0)
如果您所说的“我什么也没回来”的意思是您有一个看起来像这样的对象:
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x1111eb518>
那不是您想的那样。执行resource.syllabus_points
时,您正在做的是通过ManyToMany管理器访问与LessonResources
实例的相关 SyllabusPoints
对象。如果您调用SyllabusPoints.objects
,则会返回一个约束较少的类似对象。就像您使用任何其他模型管理器一样,您需要在该管理器上调用all
,get
或filter
之类的东西。在此示例中,我手头仅有一个Blog应用程序,其中有一个blog.Post
模型和一个blog.Tag
的ManyToManyField。都是一样的
>>> from naguine.apps import get_model
>>> Post = get_model('blog.Post')
>>> Tag = get_model('blog.Tag')
>>> p = Post.objects.create()
>>> p.tags.add(Tag.objects.create(name='testtag'))
>>> p.tags
<django.db.models.fields.related_descriptors.create_forward_many_to_many_manager.<locals>.ManyRelatedManager object at 0x1111eb518>
>>> p.tags.all()
<QuerySet [<Tag: testtag>]>
答案 2 :(得分:0)
如更新2 中所述,此问题是由Django管理界面处理多对多关系的方式引起的。
django管理界面正在使用TabularInline
管理表单,因此我可以一次编辑课程及其相关资源;
admin.py
from django.contrib import admin
from timetable.models import *
from timetable.forms import LessonForm
# Register your models here.
class ResourcesInLine(admin.TabularInline):
model = LessonResources
class LessonAdmin(admin.ModelAdmin):
form = LessonForm
inlines = [
ResourcesInLine,
]
admin.site.register(LessonSlot)
admin.site.register(TimetabledLesson)
admin.site.register(Lesson, LessonAdmin)
admin.site.register(LessonResources)
admin.site.register(LessonSuspension)
当我保存管理表单时,发生的基本顺序是:
post-save
的{{1}}信号问题是步骤5。当我保存set_syllabus_points()
表单时,课程资源的lesson
都是空白。该信号按预期方式工作并添加了它们,但是admin模块随后再次清除了它们,因为它的预期行为是使syllabus_points
实例适合初始形式上的内容-没什么!
我对此的长期解决方案是在管理界面外构建自定义表单以自己完成此操作。由于这不会在多对多关系上调用LessonResoure
方法,因此此问题将停止。
同时,我已经从管理界面中删除了clean
,以创建新课程。这意味着不会调用syllabus_points
方法,并解决了该问题。
修复 编辑管理界面以删除多对多关系,例如
clear
非常感谢@Cole和@Rajesh为我指出正确的方向!