You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

130 lines
5.7 KiB

# -*- coding: UTF-8 -*-
# Erik Stein <code@classlibrary.net>, 10/2010
from django.db import models
from django.utils.translation import ugettext_lazy as _
from .markup import DEFAULT_MARKUP_TYPES, Markup
from .widgets import MarkupTextarea, AdminMarkupTextareaWidget
_get_markup_type_field_name = lambda name: '%s_markup_type' % name
def _get_rendered_field_name(name):
field_name = '%s_rendered' % name
# Make the field internal
if not field_name[0] == '_':
field_name = '_%s' % field_name
return field_name
class MarkupDescriptor(object):
def __init__(self, field):
self.field = field
self.rendered_field_name = _get_rendered_field_name(self.field.name)
self.markup_type_field_name = _get_markup_type_field_name(self.field.name)
def __get__(self, instance, owner):
if instance is None:
raise AttributeError("Can only be accessed via an instance.")
markup = instance.__dict__[self.field.name]
markup_type = instance.__dict__[self.markup_type_field_name]
if markup_type is None:
return None
if hasattr(self.field.markup_choices_dict[markup_type], 'render'):
markup_class = self.field.markup_choices_dict[markup_type]
else:
# Just a plain filter function, use default Markup class
if markup is None:
return None
markup_class = Markup
return markup_class(instance, self.field.name, self.rendered_field_name,
self.markup_type_field_name)
def __set__(self, obj, value):
if isinstance(value, Markup):
obj.__dict__[self.field.name] = value.raw
setattr(obj, self.rendered_field_name, value.rendered)
setattr(obj, self.markup_type_field_name, value.markup_type)
else:
obj.__dict__[self.field.name] = value
class MarkupField(models.TextField):
def __init__(self, verbose_name=None, name=None, markup_type=None,
default_markup_type=None, markup_choices=DEFAULT_MARKUP_TYPES,
**kwargs):
if markup_type and default_markup_type:
raise ValueError("Cannot specify both markup_type and default_markup_type.")
# if markup_choices and not default_markup_type:
# raise ValueError('No default_markup_type specified.')
self.default_markup_type = markup_type or default_markup_type
self.markup_type_editable = markup_type is None
# pre 1.0 markup_choices might have been a dict
if isinstance(markup_choices, dict):
# raise DeprecationWarning('passing a dictionary as markup_choices is deprecated')
self.markup_choices_dict = markup_choices
self.markup_choices_list = markup_choices.keys()
else:
self.markup_choices_list = [mc[0] for mc in markup_choices]
self.markup_choices_dict = dict(markup_choices)
if (self.default_markup_type and
self.default_markup_type not in self.markup_choices_list):
raise ValueError("Invalid default_markup_type '%s' for field '%s', allowed values: %s" %
(self.default_markup_type, name, ', '.join(self.markup_choices_list)))
super(MarkupField, self).__init__(verbose_name, name, **kwargs)
def contribute_to_class(self, cls, name):
if not cls._meta.abstract:
column_name = self.db_column or name
choices = zip(self.markup_choices_list, self.markup_choices_list)
markup_type_field = models.CharField(max_length=30,
choices=choices, default=self.default_markup_type,
editable=self.markup_type_editable, blank=self.blank,
db_column=_get_markup_type_field_name(column_name))
rendered_field = models.TextField(editable=False,
db_column=_get_rendered_field_name(column_name))
markup_type_field.creation_counter = self.creation_counter+1
rendered_field.creation_counter = self.creation_counter+2
cls.add_to_class(_get_markup_type_field_name(name), markup_type_field)
cls.add_to_class(_get_rendered_field_name(name), rendered_field)
super(MarkupField, self).contribute_to_class(cls, name)
setattr(cls, self.name, MarkupDescriptor(self))
def pre_save(self, model_instance, add):
value = super(MarkupField, self).pre_save(model_instance, add)
if value.markup_type not in self.markup_choices_list:
raise ValueError('Invalid markup type (%s), allowed values: %s' %
(value.markup_type,
', '.join(self.markup_choices_list)))
if hasattr(self.markup_choices_dict[value.markup_type], 'render'):
rendered = value.render()
else:
rendered = self.markup_choices_dict[value.markup_type](value.raw)
setattr(model_instance, _get_rendered_field_name(self.attname), rendered)
return value.raw
def get_db_prep_value(self, value):
# for Django 1.2+ rename this to get_prep_value
if isinstance(value, Markup):
return value.raw
else:
return value
def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return value.raw
def formfield(self, **kwargs):
defaults = {'widget': MarkupTextarea}
defaults.update(kwargs)
return super(MarkupField, self).formfield(**defaults)
# Register MarkupField to use the custom widget in the Admin
from django.contrib.admin.options import FORMFIELD_FOR_DBFIELD_DEFAULTS
FORMFIELD_FOR_DBFIELD_DEFAULTS[MarkupField] = {'widget': AdminMarkupTextareaWidget}