首页 > 解决方案 > 基于 CharField 选择动态生成 Django 表单

问题描述

我有一个用户配置文件模型,它存储了许多第三方 API 密钥的配置,我试图确定如何最好地根据用户所做的选择动态生成表单。该应用程序仅支持一部分服务,因此我使用CharField(+ CHOICES) 来缩小用户尝试提交配置的范围。用户可以提交任意数量的副本(Cloud Service 1例如 3 组密钥)

我有这个模型:

class ServiceIntegration(models.Model):
    profile = models.ForeignKey(
        Profile,
        on_delete=models.CASCADE
    )

    CS1 = 'CS1'
    CS2 = 'CS2'
    SERVICE_CHOICES = (
        (CS1, 'Cloud Service 1'),
        (CS2, 'Cloud Service 2'),
    )
    alias = models.CharField(max_length=255)
    service = models.CharField(
        max_length=255,
        choices=SERVICE_CHOICES,
        default=CS1
    )
    config = JSONField()

在表单中,用户有一个下拉列表,其 QuerySet 设置为此模型的对象。当用户进行选择时,我想联系一个端点并将一些表单 HTML 转储到预定位置。据推测,我可以为每个集成设置一个表单,并简单地拥有一个接受选择、查找表单并将其呈现出来的视图(将数据发布到该端点也是如此)。

根据用户的选择动态呈现表单(并接受数据)的最佳选择是什么?

标签: pythondjango

解决方案


我最终编写了每个集成实现都继承自的 BaseService 类,以及每个实现返回的表单,例如:

from service_integrations.forms import CloudServiceOneForm

class BaseService(ABC):

    def __init__(self, configuration):
        self.configuration = configuration
        super().__init__()

    @abstractmethod
    def get_form(self):
        pass

    @abstractmethod
    def get_service_name(self):
        pass

    @abstractmethod
    def check(self):
        pass

    @abstractmethod
    def run(self):
        pass


class CloudServiceOne(BaseService):

    def get_service_name(self):
        return 'Cloud Service One' # This should match the CharField Choice

    def get_form(self):
        return CloudServiceOneForm()

    def check(self):
        # Requirements for config go here
        if self.configuration.get('secret_key') and self.configuration.get('access_key'):
            return True
        else:
            return False

    def run(self):
        pass

以及可以从视图调用以将相关表单传递到上下文的实用程序:

from service_integrations.classes import BaseService

def get_service(service):
    # Passes empty configurations to each subclass of BaseService and returns the handler based on the name.
    for cls in BaseService.__subclasses__():
        if cls({}).get_service_name() == service:
            return cls
    return None

然后您可以将表单传递到上下文中,例如:

service = get_service('Cloud Service One')
context = {
    'form': service.get_form()
}
return render(request, 'some_template.html', context)

推荐阅读