""" replacement for ModelForm to make certain (or all) fields display-only """ import django.newforms as forms from django.db.models.fields import FieldDoesNotExist from django.utils.encoding import force_unicode from django.utils.html import escape, conditional_escape from django.utils.safestring import mark_safe from django.utils.html import linebreaks from django.conf import settings import datetime, time from itertools import chain import markdown class DisplayModelForm(forms.ModelForm): """ Subclass of ModelForm Can be used as normal ModelForm but has the following extra's: - making form display_only: - determine the widget to use when displaying as display_only (to add links to foreign keys and M2M fields: use the links attribute) and - adding attrs to widgets - modifying help_texts in widgets - set links on FK and M2M fields when display-only (requires the target to have a get_absolute_url attribute) - reorder fields - set input and output formats on datefields uses settings.DATE_INPUT_FORMATS and settings.DATE_OUTPUT_FORMAT How to use? Render a form display_only: this renders all the fields not with form fields but with normal html. example: class MyForm(DisplayModelForm): ... use form for editing: form = MyForm(instance=myobject) use form for displaying info (all fields display_only): form = EventForm(instance=myobject, display_only = True) or make only a few field display_only: form = EventForm(instance=myobject, display_only = True, display_fields=['title','category']) to determine the widgets used when rendering as display_only add a 'displaywidgets' dict to the Meta class of the ModelForm example: class MyForm(DisplayModelForm): class Meta: displaywidgets = { 'location': DisplayTextURL(urlbase='/documents/') } # PS: do this if location is a charfield, not a foreign key # for foreign keys, use the links attribute (see further) Extra's: - add an 'attrs' dict to the Meta class, for each field a dict of attrs - add a 'help_texts' dict to Meta - add a 'links' list of fields to Meta - field ordering uses the fields list (as in ModelForm) example: class MyForm(DisplayModelForm): class Meta: model = Event attrs = { 'title': {'size': 90}, } help_texts = { 'title': 'Give me a title!' } links = ['category',] """ def __init__(self, display_only = False, display_fields=[], *args, **kwargs): super(DisplayModelForm, self).__init__(*args, **kwargs) if display_only: self._make_form_display_only(display_fields) else: self._set_defaults() # add attrs to widgets attrs = getattr(self.Meta, 'attrs', {}) for field in attrs: try: self.fields[field].widget.attrs.update(attrs[field]) except: pass # modify help_texts help_texts = getattr(self.Meta, 'help_texts', {}) for field in help_texts: try: self.fields[field].help_text = help_texts[field] except: pass # Reorder fields fields = list(getattr(self.Meta, 'fields', [])) if fields: for f in self.fields: if not f in fields: fields.append(f) self.fields.keyOrder = fields def _make_form_display_only(self, display_fields=[]): links = getattr(self.Meta, 'links', []) displaywidgets = getattr(self.Meta, 'displaywidgets', []) dfields = display_fields or self.fields for field in dfields: if not field in self.fields: raise FieldDoesNotExist, 'Item %s in display_fields is no field on the form' % field else: self.fields[field].help_text = u'' # replace widgets if field in displaywidgets: self.fields[field].widget = displaywidgets[field] else: if isinstance(self.fields[field].widget, forms.Select): if field in links: newfield = DisplayLinkedModelChoiceField(self.fields[field].queryset, widget = DisplaySelect, label = self.fields[field].label, help_text = self.fields[field].help_text, ) self.fields[field] = newfield else: self.fields[field].widget = DisplaySelect(choices=self.fields[field].widget.choices) elif isinstance(self.fields[field].widget, forms.SelectMultiple): if field in links: newfield = DisplayLinkedModelMultipleChoiceField(self.fields[field].queryset, widget = DisplaySelectMultiple, label = self.fields[field].label, help_text = self.fields[field].help_text, ) self.fields[field] = newfield else: self.fields[field].widget = DisplaySelectMultiple(choices=self.fields[field].widget.choices) elif isinstance(self.fields[field].widget, forms.Textarea): self.fields[field].widget = DisplayTextarea() elif isinstance(self.fields[field].widget, forms.TextInput): self.fields[field].widget = DisplayTextInput() elif isinstance(self.fields[field].widget, forms.CheckboxInput): self.fields[field].widget = DisplayCheckboxInput() elif isinstance(self.fields[field].widget, forms.FileInput): self.fields[field].widget = DisplayTextInput() # special cases if isinstance(self.fields[field], forms.DateField): self.fields[field].widget = DisplayTextInputDate() if isinstance(self.fields[field], forms.URLField): self.fields[field].widget = DisplayTextURL() def _set_defaults(self): for field in self.fields: if isinstance(self.fields[field], forms.DateField): self.fields[field].input_formats = settings.DATE_INPUT_FORMATS self.fields[field].widget = forms.DateTimeInput(format=settings.DATE_OUTPUT_FORMAT) # widgets class DisplayInput(forms.Widget): """ basic class display data only""" def format_value(self, value): return u'%s' % conditional_escape(force_unicode(value)) def render(self, name, value, attrs=None): if value is None: value = '' final_attrs = u'' if attrs.get('id'): final_attrs = u' id="%s"' % attrs['id'] return mark_safe(u'