Created
March 5, 2017 22:50
-
-
Save melvyn-sopacua/2e848fa5b8be0f53c2a5267ba68d570e to your computer and use it in GitHub Desktop.
Display update form in a modal with initial data
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.views import generic | |
from django.db.models.base import Model | |
from collections import OrderedDict | |
from django.forms import BaseForm | |
from django.core.validators import RegexValidator | |
__all__ = ( | |
'AdditionalFormsMixin', | |
) | |
class AdditionalFormsManager(OrderedDict): | |
# Allow only these chars to be able to call them as variables in the | |
# template. | |
_name_validator = RegexValidator( | |
regex=r'^\w+$', message='Form name contains unsupported characters' | |
) | |
def register_form(self, name: str, form_class: BaseForm, action_url: str, | |
is_multipart: bool=False, | |
model_instance: Model=None) -> None: | |
""" | |
Register a form with the manager | |
The name is validated to be useful as template variable and a | |
ValiditionError is raised if this is not the case. | |
:param name: form name, used as variable in the context | |
:param form_class: class of the form to add to the context | |
:param action_url: url the form should be submitted to | |
:param is_multipart: if the form should be capable of uploading files | |
:param model_instance: if the form is a model form used for update, | |
it will update this instance. | |
""" | |
self._name_validator(name) | |
formdata = { | |
'form_class': form_class, | |
'action_url': action_url, | |
'is_multipart': is_multipart, | |
'model_instance': model_instance, | |
} | |
self[name] = formdata | |
def get_additional_form(self, name: str) -> tuple: | |
try: | |
formdata = self.get(name) | |
except KeyError: | |
raise KeyError('No form with name {} found.'.format(name)) | |
form_class = formdata.pop('form_class') | |
instance = formdata.pop('model_instance') | |
form = form_class(instance=instance) if instance else form_class() | |
return form, formdata | |
def get_context_update(self) -> dict: | |
update = {} | |
for name in self.keys(): | |
form, data = self.get_additional_form(name) | |
update[name] = form | |
for prop in data: | |
propname = '{}__{}'.format(name, prop) | |
update[propname] = data[prop] | |
return update | |
class AdditionalFormsMixin(generic.base.ContextMixin): | |
def __init__(self, *args, **kwargs): | |
self._afm = AdditionalFormsManager() | |
super().__init__(*args, **kwargs) | |
def add_additional_form(self, name: str, form_class: BaseForm, | |
action_url: str, **kwargs) -> None: | |
""" | |
Convenience proxy to `AdditionalFormsManager.register_form()`. | |
Also forwards-compatible with any additional keyword arguments it may | |
choose to support. | |
:param name: form name, used as variable in the context | |
:param form_class: class of the form to add to the context | |
:param action_url: url the form should be submitted to | |
""" | |
self._afm.register_form(name, form_class, action_url, **kwargs) | |
def get_context_data(self, **kwargs): | |
context = super().get_context_data(**kwargs) | |
context.update(self._afm.get_context_update()) | |
return context | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{% extends "base.html" %} | |
{% load l10n bootstrap3 %} | |
{% block form_media %}{{ project_update_form.media }}{% endblock %} | |
{% block content %} | |
{# deleted for brevity #} | |
<nav class="col-md-2 col-md-offset-1" role="navigation"> | |
<ul class="nav nav-stacked nav-pills"> | |
<li role="presentation"> | |
<a href="#project-update" data-toggle="modal">Edit</a> | |
</li> | |
</ul> | |
</nav> | |
<div class="modal fade" id="project-update"> | |
<div class="modal-dialog"> | |
<div class="modal-content"> | |
<form class="form-horizontal" action="{{ project_update_form__action_url }}" method="post"> | |
{% csrf_token %} | |
<div class="modal-header"> | |
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> | |
<h4 class="modal-title">Edit project</h4> | |
</div> | |
<div class="modal-body"> | |
{% bootstrap_form project_update_form layout="horizontal" %} | |
</div> | |
<div class="modal-footer"> | |
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button> | |
<button type="submit" class="btn btn-primary">Save changes</button> | |
</div> | |
</form> | |
</div><!-- /.modal-content --> | |
</div><!-- /.modal-dialog --> | |
</div><!-- /.modal --> | |
{% endblock %} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.views import generic | |
from django.utils.translation import ugettext_lazy as _ | |
from .. import models, forms | |
from .mixins import AdditionalFormsMixin | |
from django.urls import reverse | |
class ProjectDetailView(AdditionalFormsMixin, generic.DetailView): | |
model = models.Project | |
context_object_name = 'project' | |
template_name = 'view/project.html' | |
def get(self, request, *args, **kwargs): | |
instance = self.get_object() | |
url = reverse('project_edit', kwargs={'slug': instance.slug}) | |
self.add_additional_form( | |
'project_update_form', forms.ProjectForm, url, | |
model_instance=instance | |
) | |
return super().get(request, *args, **kwargs) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment