Created
June 23, 2010 15:16
-
-
Save jezdez/450067 to your computer and use it in GitHub Desktop.
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
| diff --git a/cms/models/pluginmodel.py b/cms/models/pluginmodel.py | |
| index fc318a9..108a165 100644 | |
| --- a/cms/models/pluginmodel.py | |
| +++ b/cms/models/pluginmodel.py | |
| @@ -5,7 +5,8 @@ from cms.plugin_rendering import PluginContext, PluginRenderer | |
| from cms.exceptions import DontUsePageAttributeWarning | |
| from publisher import MpttPublisher | |
| from django.db import models | |
| -from django.db.models.base import ModelBase | |
| +from django.db.models.base import ModelBase, model_unpickle, simple_class_factory | |
| +from django.db.models.query_utils import DeferredAttribute | |
| from django.utils.translation import ugettext_lazy as _ | |
| from django.core.exceptions import ValidationError, ObjectDoesNotExist | |
| from django.conf import settings | |
| @@ -18,6 +19,9 @@ class PluginModelBase(ModelBase): | |
| Metaclass for all plugins. | |
| """ | |
| def __new__(cls, name, bases, attrs): | |
| + render_meta = attrs.pop('RenderMeta', None) | |
| + if render_meta is not None: | |
| + attrs['_render_meta'] = render_meta() | |
| new_class = super(PluginModelBase, cls).__new__(cls, name, bases, attrs) | |
| found = False | |
| bbases = bases | |
| @@ -27,32 +31,32 @@ class PluginModelBase(ModelBase): | |
| found = True | |
| bbases = False | |
| else: | |
| - bbases = bcls.__bases__ | |
| + bbases = bcls.__bases__ | |
| if found: | |
| if new_class._meta.db_table.startswith("%s_" % new_class._meta.app_label): | |
| table = "cmsplugin_" + new_class._meta.db_table.split("%s_" % new_class._meta.app_label, 1)[1] | |
| new_class._meta.db_table = table | |
| - return new_class | |
| - | |
| - | |
| + return new_class | |
| + | |
| + | |
| class CMSPlugin(MpttPublisher): | |
| __metaclass__ = PluginModelBase | |
| - | |
| + | |
| placeholder = models.ForeignKey(Placeholder, editable=False, null=True) | |
| parent = models.ForeignKey('self', blank=True, null=True, editable=False) | |
| position = models.PositiveSmallIntegerField(_("position"), blank=True, null=True, editable=False) | |
| language = models.CharField(_("language"), max_length=5, blank=False, db_index=True, editable=False) | |
| plugin_type = models.CharField(_("plugin_name"), max_length=50, db_index=True, editable=False) | |
| creation_date = models.DateTimeField(_("creation date"), editable=False, default=datetime.now) | |
| - | |
| + | |
| level = models.PositiveIntegerField(db_index=True, editable=False) | |
| lft = models.PositiveIntegerField(db_index=True, editable=False) | |
| rght = models.PositiveIntegerField(db_index=True, editable=False) | |
| tree_id = models.PositiveIntegerField(db_index=True, editable=False) | |
| - | |
| + | |
| class Meta: | |
| app_label = 'cms' | |
| - | |
| + | |
| class PublisherMeta: | |
| exclude_fields = [] | |
| exclude_fields_append = ['plugin_ptr'] | |
| @@ -62,24 +66,59 @@ class CMSPlugin(MpttPublisher): | |
| total = 1 | |
| text_enabled = False | |
| - def __init__(self, *args, **kwargs): | |
| - self._render_meta = self.RenderMeta() | |
| - super(CMSPlugin, self).__init__(*args, **kwargs) | |
| + def __reduce__(self): | |
| + """ | |
| + Provide pickling support. Normally, this just dispatches to Python's | |
| + standard handling. However, for models with deferred field loading, we | |
| + need to do things manually, as they're dynamically created classes and | |
| + only module-level classes can be pickled by the default path. | |
| + """ | |
| + data = self.__dict__ | |
| + model = self.__class__ | |
| + # The obvious thing to do here is to invoke super().__reduce__() | |
| + # for the non-deferred case. Don't do that. | |
| + # On Python 2.4, there is something wierd with __reduce__, | |
| + # and as a result, the super call will cause an infinite recursion. | |
| + # See #10547 and #12121. | |
| + defers = [] | |
| + pk_val = None | |
| + if self._deferred: | |
| + factory = deferred_class_factory | |
| + for field in self._meta.fields: | |
| + if isinstance(self.__class__.__dict__.get(field.attname), | |
| + DeferredAttribute): | |
| + defers.append(field.attname) | |
| + if pk_val is None: | |
| + # The pk_val and model values are the same for all | |
| + # DeferredAttribute classes, so we only need to do this | |
| + # once. | |
| + obj = self.__class__.__dict__[field.attname] | |
| + model = obj.model_ref() | |
| + else: | |
| + factory = simple_class_factory | |
| + return (model_unpickle, (model, defers, factory), data) | |
| def __unicode__(self): | |
| return unicode(self.id) | |
| - | |
| + | |
| + class Meta: | |
| + app_label = 'cms' | |
| + | |
| + class PublisherMeta: | |
| + exclude_fields = [] | |
| + exclude_fields_append = ['plugin_ptr'] | |
| + | |
| def get_plugin_name(self): | |
| from cms.plugin_pool import plugin_pool | |
| return plugin_pool.get_plugin(self.plugin_type).name | |
| - | |
| + | |
| def get_short_description(self): | |
| - return self.get_plugin_instance()[0].__unicode__() | |
| - | |
| + return self.get_plugin_instance()[0].__unicode__() | |
| + | |
| def get_plugin_class(self): | |
| from cms.plugin_pool import plugin_pool | |
| return plugin_pool.get_plugin(self.plugin_type) | |
| - | |
| + | |
| def get_plugin_instance(self, admin=None): | |
| from cms.plugin_pool import plugin_pool | |
| plugin_class = plugin_pool.get_plugin(self.plugin_type) | |
| @@ -100,7 +139,7 @@ class CMSPlugin(MpttPublisher): | |
| else: | |
| instance = self | |
| return instance, plugin | |
| - | |
| + | |
| def render_plugin(self, context=None, placeholder=None, admin=False, processors=None): | |
| instance, plugin = self.get_plugin_instance() | |
| if instance and not (admin and not plugin.admin_preview): | |
| @@ -120,7 +159,7 @@ class CMSPlugin(MpttPublisher): | |
| renderer = PluginRenderer(context, instance, placeholder, template, processors) | |
| return renderer.content | |
| return "" | |
| - | |
| + | |
| def get_media_path(self, filename): | |
| pages = self.placeholder.page_set.all() | |
| if pages.count(): | |
| @@ -128,7 +167,7 @@ class CMSPlugin(MpttPublisher): | |
| else: # django 1.0.2 compatibility | |
| today = date.today() | |
| return join(settings.CMS_PAGE_MEDIA_PATH, str(today.year), str(today.month), str(today.day), filename) | |
| - | |
| + | |
| @property | |
| def page(self): | |
| warnings.warn( | |
| @@ -136,7 +175,7 @@ class CMSPlugin(MpttPublisher): | |
| "guaranteed to have a page associated with them!", | |
| DontUsePageAttributeWarning) | |
| return get_page_from_placeholder_if_exists(self.placeholder) | |
| - | |
| + | |
| def get_instance_icon_src(self): | |
| """ | |
| Get src URL for instance's icon | |
| @@ -156,22 +195,22 @@ class CMSPlugin(MpttPublisher): | |
| return unicode(plugin.icon_alt(instance)) | |
| else: | |
| return u'' | |
| - | |
| + | |
| def save(self, no_signals=False, *args, **kwargs): | |
| if no_signals:# ugly hack because of mptt | |
| super(CMSPlugin, self).save_base(cls=self.__class__) | |
| else: | |
| super(CMSPlugin, self).save() | |
| - | |
| - | |
| + | |
| + | |
| def set_base_attr(self, plugin): | |
| for attr in ['parent_id', 'placeholder', 'language', 'plugin_type', 'creation_date', 'level', 'lft', 'rght', 'position', 'tree_id']: | |
| setattr(plugin, attr, getattr(self, attr)) | |
| - | |
| + | |
| def _publisher_get_public_copy(self): | |
| """Overrides publisher public copy acessor, because of the special | |
| kind of relation between Plugins. | |
| - """ | |
| + """ | |
| publisher_public = self.publisher_public | |
| if not publisher_public: | |
| return | |
| @@ -188,7 +227,7 @@ class CMSPlugin(MpttPublisher): | |
| setattr(public_copy, field.name, value) | |
| public_copy.publisher_is_draft=False | |
| return public_copy | |
| - | |
| + | |
| def copy_plugin(self, target_placeholder, target_language, plugin_tree): | |
| """ | |
| Copy this plugin and return the new plugin. | |
| @@ -233,12 +272,12 @@ class CMSPlugin(MpttPublisher): | |
| plugin_instance.save() | |
| self.copy_relations(new_plugin, plugin_instance) | |
| return new_plugin | |
| - | |
| + | |
| def copy_relations(self, new_plugin, plugin_instance): | |
| """ | |
| Handle copying of any relations attached to this plugin | |
| """ | |
| - | |
| + | |
| def has_change_permission(self, request): | |
| page = get_page_from_placeholder_if_exists(self.placeholder) | |
| if page: | |
| @@ -248,16 +287,16 @@ class CMSPlugin(MpttPublisher): | |
| elif self.parent: | |
| return self.parent.has_change_permission(request) | |
| return False | |
| - | |
| + | |
| def is_first_in_placeholder(self): | |
| return self.position == 0 | |
| - | |
| + | |
| def is_last_in_placeholder(self): | |
| """ | |
| WARNING: this is a rather expensive call compared to is_first_in_placeholder! | |
| """ | |
| return self.placeholder.cmsplugin_set.all().order_by('-position')[0].pk == self.pk | |
| - | |
| + | |
| def get_position_in_placeholder(self): | |
| """ | |
| 1 based position! | |
| @@ -265,3 +304,38 @@ class CMSPlugin(MpttPublisher): | |
| return self.position + 1 | |
| reversion_register(CMSPlugin) | |
| + | |
| +def deferred_class_factory(model, attrs): | |
| + """ | |
| + Returns a class object that is a copy of "model" with the specified "attrs" | |
| + being replaced with DeferredAttribute objects. The "pk_value" ties the | |
| + deferred attributes to a particular instance of the model. | |
| + """ | |
| + class Meta: | |
| + pass | |
| + setattr(Meta, "proxy", True) | |
| + setattr(Meta, "app_label", model._meta.app_label) | |
| + | |
| + class RenderMeta: | |
| + pass | |
| + setattr(RenderMeta, "index", model._render_meta.index) | |
| + setattr(RenderMeta, "total", model._render_meta.total) | |
| + setattr(RenderMeta, "text_enabled", model._render_meta.text_enabled) | |
| + | |
| + # The app_cache wants a unique name for each model, otherwise the new class | |
| + # won't be created (we get an old one back). Therefore, we generate the | |
| + # name using the passed in attrs. It's OK to reuse an old case if the attrs | |
| + # are identical. | |
| + name = "%s_Deferred_%s" % (model.__name__, '_'.join(sorted(list(attrs)))) | |
| + | |
| + overrides = dict([(attr, DeferredAttribute(attr, model)) | |
| + for attr in attrs]) | |
| + overrides["Meta"] = RenderMeta | |
| + overrides["RenderMeta"] = RenderMeta | |
| + overrides["__module__"] = model.__module__ | |
| + overrides["_deferred"] = True | |
| + return type(name, (model,), overrides) | |
| + | |
| +# The above function is also used to unpickle model instances with deferred | |
| +# fields. | |
| +deferred_class_factory.__safe_for_unpickling__ = True |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment