简体   繁体   中英

Dynamically generating Django form based on CharField choice

I have a user profile model that is storing configurations for a number of third party API keys, and I'm trying to determine how to best go about dynamically generating forms based on the choice that the user makes. The app supports only a subset of services, so I'm using a CharField (+ CHOICES ) to narrow down what the user is trying to submit a configuration for. The user can submit as many duplicates as they would like (3 sets of Cloud Service 1 keys, for example)

I have this model:

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()

In a form, the user has a dropdown whose QuerySet is set to this model's objects. When the user makes a selection, I'd like to reach out to an endpoint and dump some form HTML in a predetermined location. Presumably, I could have a form set up for each integration, and simply have a view that takes in the choice, looks up the form, and renders it back out (same for POSTing data to that endpoint).

What's my best bet to render forms (and accept data) dynamically based on a user's choice?

I ended up writing a BaseService class that each integration implementation inherits from, and a form that each implementation returns like:

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

As well as a utility that can be called from a view to pass the relevant form into a context:

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

Then you can pass the form into the context like:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM