Sam*_* B. 6 python django plugins
我使用 Django REST 作为后端应用程序制作了一个学习管理系统。该应用程序已经发展了很多,其用户群也有了很大的增长,越来越多的人对它表现出了兴趣。
我意识到最先进的 LMS 应用程序有一些共同点:模块化——人们可以开发插件、扩展并轻松添加功能。我的应用程序是整体式的,实际上不允许这样做,因此我正在考虑重新设计代码库,以便对扩展更加开放;特别是,我正在尝试适应插件系统。
在 Django 中你会如何处理类似的事情?即使是一些一般性的想法,不一定是 Django 或 Python 特定的,也是非常受欢迎的。
我将给出一个需要从核心硬编码转变为可插入功能的示例。
考虑以下模型:
class Exercise(TimestampableModel, OrderableModel, LockableModel):
"""
An Exercise represents a question, coding problem, or other element that can appear inside of an exam.
"""
MULTIPLE_CHOICE = 1
OPEN_ANSWER = 2
CLOZE = 3
PROGRAMMING = 4
EXERCISE_TYPES = (
(MULTIPLE_CHOICE "Multiple choice"),
(OPEN_ANSWER, "Open answer"),
(CLOZE, "Cloze"),
(PROGRAMMING, "Programming exercise"),
)
course = models.ForeignKey(
Course,
on_delete=models.PROTECT,
related_name="exercises",
)
exercise_type = models.PositiveSmallIntegerField(choices=EXERCISE_TYPES)
name = models.CharField(max_length=75, blank=True)
text = models.TextField(blank=True)
Run Code Online (Sandbox Code Playgroud)
目前,练习的类型是在有选择的领域中硬编码的。该Exercise模型具有一些属性和方法,可以实现诸如获得该练习的最高可达到分数或对该练习的答案进行评分之类的操作。这些方法包含基于exercise_type. 例如:
def get_max_score(self):
if self.exercise_type == Exercise.PROGRAMMING:
return self.testcases.count()
if self.exercise_type == Exercise.MULTIPLE_CHOICE:
max_score = (self.choices.all().aggregate(Max("correctness")))[
"correctness__max"
]
return max_score
# ...
Run Code Online (Sandbox Code Playgroud)
现在,假设我想将编程练习制作成插件。
你会如何处理这个问题?
我首先想到的是需要有一个间接层来获取可用的练习类型。不必在exercise_type数据库级别检查字段的一组选择的成员资格,而是必须在运行时验证该值并遵循一些约定,告诉我的应用程序在插件内搜索该类型,例如某处会有一些load_exercise_types功能。
然后,业务逻辑必须移动到可以动态调用练习类型的正确代码的位置。
例如,我可以使用ExerciseBusinessLogic静态方法创建一个抽象基类from_model_instance——插件开发人员将对其进行子类化并实现相关方法(如我get_max_score上面所示),并且模型实例将负责实例化基于正确的子类关于运动类型。
我看了一下这篇文章,它展示了如何在 Django 中实现一个简单的插件系统的概念证明,但它似乎限制了这样一个插件的功能;在这里,我正在寻找更复杂的解决方案,这些解决方案允许添加模型、使用新功能扩展现有模型,并扩展现有的核心业务逻辑,从而实现更多与应用程序中其他内容集成的操作。
您将如何解决这个问题?
这取决于插件是否只有逻辑(并且可以使用与基础系统相同的持久数据结构)或需要添加自己的持久字段。
Exercise将定义任何练习类型插件都需要遵守的 API。例如,该 API 需要get_max_score具有特定签名和含义的方法。
EXERCISE_TYPESDjango 设置中。Exercise读取该设置并收集它们各自声明的类型。Exercise实例化它并通过委托调用它的函数。或者,此类插件类型可以简单地是 的子类Exercise。
这会涉及更多。我认为最简单的方法是插件M为其需要的所有附加字段声明自己的模型,并Exercise使用 aGenericForeignKey来存储与实例的一对一关系M。
Exercise然后可以根据需要调用适当的加载/存储逻辑。