#!/usr/bin/env python # -*- coding: UTF-8 -*- import six from collections import OrderedDict from django.apps import apps try: from django.utils.text import camel_case_to_spaces except ImportError: from django.db.models.options import get_verbose_name as camel_case_to_spaces from django.db import models class Options(object): def __init__(self, meta, help_text=None): self.use_sites = meta.pop('use_sites', False) self.use_i18n = meta.pop('use_i18n', False) self.use_subdomains = meta.pop('use_subdomains', False) self.use_redirect = meta.pop('use_redirect', False) self.use_cache = meta.pop('use_cache', False) self.groups = meta.pop('groups', {}) self.seo_views = meta.pop('seo_views', []) self.verbose_name = meta.pop('verbose_name', None) self.verbose_name_plural = meta.pop('verbose_name_plural', None) self.backends = list(meta.pop('backends', ('path', 'modelinstance', 'model', 'view'))) self._set_seo_models(meta.pop('seo_models', [])) self.bulk_help_text = help_text self.original_meta = meta self.models = OrderedDict() self.name = None self.elements = None self.metadata = None def get_model(self, name): try: return self.models[name] except KeyError: return None def _update_from_name(self, name): self.name = name self.verbose_name = self.verbose_name or camel_case_to_spaces(name) self.verbose_name_plural = self.verbose_name_plural or self.verbose_name + 's' def _register_elements(self, elements): """ Takes elements from the metadata class and creates a base model for all backend models . """ self.elements = elements for key, obj in elements.items(): obj.contribute_to_class(self.metadata, key) # Create the common Django fields fields = {} for key, obj in elements.items(): if obj.editable: field = obj.get_field() if not field.help_text: field.help_text = self.bulk_help_text.get(key) fields[key] = field # 0. Abstract base model with common fields base_meta = type('Meta', (), self.original_meta) class BaseMeta(base_meta): abstract = True app_label = 'seo' fields['Meta'] = BaseMeta # Do we need this? fields['__module__'] = __name__ # attrs['__module__'] self.MetadataBaseModel = type('%sBase' % self.name, (models.Model,), fields) def _add_backend(self, backend): """ Builds a subclass model for the given backend """ md_type = backend.verbose_name base = backend().get_model(self) # TODO: Rename this field new_md_attrs = {'_metadata': self.metadata, '__module__': __name__} new_md_meta = {} new_md_meta['verbose_name'] = '%s (%s)' % (self.verbose_name, md_type) new_md_meta['verbose_name_plural'] = '%s (%s)' % (self.verbose_name_plural, md_type) new_md_meta['unique_together'] = base._meta.unique_together new_md_attrs['Meta'] = type("Meta", (), new_md_meta) new_md_attrs['_metadata_type'] = backend.name if six.PY2: md_type = str(md_type) model = type("%s%s" % (self.name, "".join(md_type.split())), (base, self.MetadataBaseModel), new_md_attrs.copy()) self.models[backend.name] = model # This is a little dangerous, but because we set __module__ to __name__, the model needs tobe accessible here globals()[model.__name__] = model def _set_seo_models(self, value): """ Gets the actual models to be used. """ seo_models = [] for model_name in value: if "." in model_name: app_label, model_name = model_name.split(".", 1) model = apps.get_model(app_label, model_name) if model: seo_models.append(model) else: app = apps.get_app_config(model_name) if app: seo_models.extend(app.get_models()) # This fix the trouble on Django 1.9 when django-seo conflicts with session model seo_models = [ model for model in seo_models if model._meta.model_name != 'session' and model._meta.app_label != 'sessions' ] self.seo_models = seo_models