def contribute_to_class(self, model, name): super(TreeManager, self).contribute_to_class(model, name) if not model._meta.abstract: self.tree_model = _get_tree_model(model) self._base_manager = None if self.tree_model is not model: # _base_manager is the treemanager on tree_model self._base_manager = self.tree_model._tree_manager
def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError( _("register() expects a Django model class argument")) if not hasattr(cls, '_mptt_meta'): cls._mptt_meta = MPTTOptions(**kwargs) abstract = getattr(cls._meta, 'abstract', False) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) if _get_tree_model(cls) is cls: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, '_meta'): existing_field_names.update( [f.name for f in base._meta.local_fields]) for key in ('left_attr', 'right_attr', 'tree_id_attr', 'level_attr'): field_name = getattr(cls._mptt_meta, key) if field_name not in existing_field_names: field = models.PositiveIntegerField(db_index=True, editable=False) field.contribute_to_class(cls, field_name) # Add a tree manager, if there isn't one already if not abstract: manager = getattr(cls, 'objects', None) if manager is None: manager = cls._default_manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') elif manager.model != cls: # manager was inherited manager = manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') # make sure we have a tree manager somewhere tree_manager = None attrs = dir(cls) if "objects" in attrs and isinstance(cls.objects, TreeManager): tree_manager = cls.objects # Go look for it somewhere else else: for attr in sorted(attrs): try: # HACK: avoid using getattr(cls, attr) # because it calls __get__ on descriptors, which can cause nasty side effects # with more inconsiderate apps. # (e.g. django-tagging's TagField is a descriptor which can do db queries on getattr) # ( ref: http://stackoverflow.com/questions/27790344 ) obj = cls.__dict__[attr] except KeyError: continue if isinstance(obj, TreeManager): tree_manager = obj # prefer any locally defined manager (i.e. keep going if not local) if obj.model is cls: break if tree_manager and tree_manager.model is not cls: tree_manager = tree_manager._copy_to_model(cls) elif tree_manager is None: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, '_tree_manager') # avoid using ManagerDescriptor, so instances can refer to self._tree_manager setattr(cls, '_tree_manager', tree_manager) return cls
def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError( _("register() expects a Django model class argument")) if not hasattr(cls, '_mptt_meta'): cls._mptt_meta = MPTTOptions(**kwargs) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) if _get_tree_model(cls) is cls: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, '_meta'): existing_field_names.update( [f.name for f in base._meta.local_fields]) for key in ('left_attr', 'right_attr', 'tree_id_attr', 'level_attr'): field_name = getattr(cls._mptt_meta, key) if field_name not in existing_field_names: field = models.PositiveIntegerField(db_index=True, editable=False) field.contribute_to_class(cls, field_name) # Add a tree manager, if there isn't one already # NOTE: Django 1.10 lets models inherit managers without handholding. if django.VERSION < (1, 10) and not cls._meta.abstract: manager = getattr(cls, 'objects', None) if manager is None: manager = cls._default_manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') elif manager.model != cls: # manager was inherited manager = manager._copy_to_model(cls) manager.contribute_to_class(cls, 'objects') # make sure we have a tree manager somewhere tree_manager = None if hasattr(cls._meta, 'concrete_managers'): # Django < 1.10 cls_managers = cls._meta.concrete_managers + cls._meta.abstract_managers cls_managers = [r[2] for r in cls_managers] else: cls_managers = cls._meta.managers for cls_manager in cls_managers: if isinstance(cls_manager, TreeManager): # prefer any locally defined manager (i.e. keep going if not local) if cls_manager.model is cls: tree_manager = cls_manager break if tree_manager and tree_manager.model is not cls: tree_manager = tree_manager._copy_to_model(cls) elif tree_manager is None: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, '_default_manager') return cls
def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError(_("register() expects a Django model class argument")) if not hasattr(cls, "_mptt_meta"): cls._mptt_meta = MPTTOptions(**kwargs) abstract = getattr(cls._meta, "abstract", False) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) is_cls_tree_model = _get_tree_model(cls) is cls if is_cls_tree_model: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, "_meta"): existing_field_names.update( [f.name for f in base._meta.local_fields] ) mptt_meta = cls._mptt_meta indexed_attrs = (mptt_meta.tree_id_attr,) field_names = ( mptt_meta.left_attr, mptt_meta.right_attr, mptt_meta.tree_id_attr, mptt_meta.level_attr, ) for field_name in field_names: if field_name not in existing_field_names: field = models.PositiveIntegerField( db_index=field_name in indexed_attrs, editable=False ) field.contribute_to_class(cls, field_name) # Add an index_together on tree_id_attr and left_attr, as these are very # commonly queried (pretty much all reads). index_together = (cls._mptt_meta.tree_id_attr, cls._mptt_meta.left_attr) if index_together not in cls._meta.index_together: cls._meta.index_together += (index_together,) # Add a tree manager, if there isn't one already if not abstract: # make sure we have a tree manager somewhere tree_manager = None # Use the default manager defined on the class if any if cls._default_manager and isinstance( cls._default_manager, TreeManager ): tree_manager = cls._default_manager else: for cls_manager in cls._meta.managers: if isinstance(cls_manager, TreeManager): # prefer any locally defined manager (i.e. keep going if not local) if cls_manager.model is cls: tree_manager = cls_manager break if is_cls_tree_model: idx_together = ( cls._mptt_meta.tree_id_attr, cls._mptt_meta.left_attr, ) if idx_together not in cls._meta.index_together: cls._meta.index_together += (idx_together,) if tree_manager and tree_manager.model is not cls: tree_manager = tree_manager._copy_to_model(cls) elif tree_manager is None: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, "_tree_manager") # avoid using ManagerDescriptor, so instances can refer to self._tree_manager setattr(cls, "_tree_manager", tree_manager) return cls
def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError(_("register() expects a Django model class argument")) if not hasattr(cls, "_mptt_meta"): cls._mptt_meta = MPTTOptions(**kwargs) abstract = getattr(cls._meta, "abstract", False) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) if _get_tree_model(cls) is cls: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, "_meta"): existing_field_names.update([f.name for f in base._meta.local_fields]) for key in ("left_attr", "right_attr", "tree_id_attr", "level_attr"): field_name = getattr(cls._mptt_meta, key) if field_name not in existing_field_names: field = models.PositiveIntegerField(db_index=True, editable=False) field.contribute_to_class(cls, field_name) # Add a tree manager, if there isn't one already if not abstract: manager = getattr(cls, "objects", None) if manager is None: manager = cls._default_manager._copy_to_model(cls) manager.contribute_to_class(cls, "objects") elif manager.model != cls: # manager was inherited manager = manager._copy_to_model(cls) manager.contribute_to_class(cls, "objects") # make sure we have a tree manager somewhere tree_manager = None attrs = dir(cls) if "objects" in attrs and isinstance(cls.objects, TreeManager): tree_manager = cls.objects # Go look for it somewhere else else: for attr in sorted(attrs): try: # HACK: avoid using getattr(cls, attr) # because it calls __get__ on descriptors, which can cause nasty side effects # with more inconsiderate apps. # (e.g. django-tagging's TagField is a descriptor which can do db queries on getattr) # ( ref: http://stackoverflow.com/questions/27790344 ) obj = cls.__dict__[attr] except KeyError: continue if isinstance(obj, TreeManager): tree_manager = obj # prefer any locally defined manager (i.e. keep going if not local) if obj.model is cls: break if tree_manager and tree_manager.model is not cls: tree_manager = tree_manager._copy_to_model(cls) elif tree_manager is None: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, "_tree_manager") # avoid using ManagerDescriptor, so instances can refer to self._tree_manager setattr(cls, "_tree_manager", tree_manager) return cls
def register(meta, cls, **kwargs): """ For the weird cases when you need to add tree-ness to an *existing* class. For other cases you should subclass MPTTModel instead of calling this. """ if not issubclass(cls, models.Model): raise ValueError(_("register() expects a Django model class argument")) if not hasattr(cls, '_mptt_meta'): cls._mptt_meta = MPTTOptions(**kwargs) abstract = getattr(cls._meta, 'abstract', False) try: MPTTModel except NameError: # We're defining the base class right now, so don't do anything # We only want to add this stuff to the subclasses. # (Otherwise if field names are customized, we'll end up adding two # copies) pass else: if not issubclass(cls, MPTTModel): bases = list(cls.__bases__) # strip out bases that are strict superclasses of MPTTModel. # (i.e. Model, object) # this helps linearize the type hierarchy if possible for i in range(len(bases) - 1, -1, -1): if issubclass(MPTTModel, bases[i]): del bases[i] bases.insert(0, MPTTModel) cls.__bases__ = tuple(bases) if _get_tree_model(cls) is cls: # HACK: _meta.get_field() doesn't work before AppCache.ready in Django>=1.8 # ( see https://code.djangoproject.com/ticket/24231 ) # So the only way to get existing fields is using local_fields on all superclasses. existing_field_names = set() for base in cls.mro(): if hasattr(base, '_meta'): existing_field_names.update([f.name for f in base._meta.local_fields]) for key in ('left_attr', 'right_attr', 'tree_id_attr', 'level_attr'): field_name = getattr(cls._mptt_meta, key) if field_name not in existing_field_names: field = models.PositiveIntegerField(db_index=True, editable=False) field.contribute_to_class(cls, field_name) # Add a tree manager, if there isn't one already if not abstract: # make sure we have a tree manager somewhere tree_manager = None if hasattr(cls._meta, 'concrete_managers'): # Django < 1.10 cls_managers = cls._meta.concrete_managers + cls._meta.abstract_managers cls_managers = [r[2] for r in cls_managers] else: cls_managers = cls._meta.managers for cls_manager in cls_managers: if isinstance(cls_manager, TreeManager): # prefer any locally defined manager (i.e. keep going if not local) if cls_manager.model is cls: tree_manager = cls_manager break if tree_manager and tree_manager.model is not cls: tree_manager = tree_manager._copy_to_model(cls) elif tree_manager is None: tree_manager = TreeManager() tree_manager.contribute_to_class(cls, '_tree_manager') # avoid using ManagerDescriptor, so instances can refer to self._tree_manager setattr(cls, '_tree_manager', tree_manager) return cls
def tree_model(self): return _get_tree_model(self.model)