forked from rc/aircox
		
	RelatedPost: mapping['thread'] now manage relation between thread and parent of related object
This commit is contained in:
		@ -82,7 +82,37 @@ class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
    """
 | 
			
		||||
    Metaclass for RelatedPost children.
 | 
			
		||||
    """
 | 
			
		||||
    def __new__ (cls, name, bases, attrs):
 | 
			
		||||
    registry = {}
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def register (cl, key, model):
 | 
			
		||||
        """
 | 
			
		||||
        Register a model and return the key under which it is registered.
 | 
			
		||||
        Raise a ValueError if another model is yet associated under this key.
 | 
			
		||||
        """
 | 
			
		||||
        if key in cl.registry and cl.registry[key] is not model:
 | 
			
		||||
            raise ValueError('A model has yet been registered with "{}"'
 | 
			
		||||
                             .format(key))
 | 
			
		||||
        cl.registry[key] = model
 | 
			
		||||
        return key
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def check_thread_mapping (cl, relation, model, field):
 | 
			
		||||
        """
 | 
			
		||||
        Add information related to the mapping 'thread' info.
 | 
			
		||||
        """
 | 
			
		||||
        if not field:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        parent_model = model._meta.get_field(field).rel.to
 | 
			
		||||
        thread_model = cl.registry.get(parent_model)
 | 
			
		||||
 | 
			
		||||
        if not thread_model:
 | 
			
		||||
            raise ValueError('no registered RelatedPost for the model {}'
 | 
			
		||||
                             .format(model.__name__))
 | 
			
		||||
        relation.thread_model = thread_model
 | 
			
		||||
 | 
			
		||||
    def __new__ (cl, name, bases, attrs):
 | 
			
		||||
        rel = attrs.get('Relation')
 | 
			
		||||
        rel = (rel and rel.__dict__) or {}
 | 
			
		||||
 | 
			
		||||
@ -96,9 +126,17 @@ class RelatedPostBase (models.base.ModelBase):
 | 
			
		||||
        if name is not 'RelatedPost':
 | 
			
		||||
            _relation = RelatedPost.Relation()
 | 
			
		||||
            _relation.__dict__.update(rel)
 | 
			
		||||
            mapping = rel.get('mapping')
 | 
			
		||||
            cl.check_thread_mapping(
 | 
			
		||||
                _relation,
 | 
			
		||||
                related_model,
 | 
			
		||||
                mapping and mapping.get('thread')
 | 
			
		||||
            )
 | 
			
		||||
            attrs['_relation'] = _relation
 | 
			
		||||
 | 
			
		||||
        return super().__new__(cls, name, bases, attrs)
 | 
			
		||||
        model = super().__new__(cl, name, bases, attrs)
 | 
			
		||||
        cl.register(related_model, model)
 | 
			
		||||
        return model
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
@ -108,25 +146,80 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    class Relation:
 | 
			
		||||
        related_model = None
 | 
			
		||||
        mapping = None          # dict of related mapping values
 | 
			
		||||
        bind_mapping = False    # update fields of related data on save
 | 
			
		||||
        """
 | 
			
		||||
        Relation descriptor used to generate and manage the related object.
 | 
			
		||||
 | 
			
		||||
        * model: model of the related object
 | 
			
		||||
        * mapping: values that are bound between the post and the related
 | 
			
		||||
            object. When the post is saved, these fields are updated on it.
 | 
			
		||||
            It is a dict of { post_attr: rel_attr }
 | 
			
		||||
 | 
			
		||||
            If there is a post_attr "thread", the corresponding rel_attr is used
 | 
			
		||||
            to update the post thread to the correct Post model.
 | 
			
		||||
        """
 | 
			
		||||
        model = None
 | 
			
		||||
        mapping = None          # values to map { post_attr: rel_attr }
 | 
			
		||||
        bind = False            # update fields of related data on save
 | 
			
		||||
        thread_model = None
 | 
			
		||||
 | 
			
		||||
    def get_attribute (self, attr):
 | 
			
		||||
        attr = self._relation.mappings.get(attr)
 | 
			
		||||
        return self.related.__dict__[attr] if attr else None
 | 
			
		||||
 | 
			
		||||
    def update_thread_mapping (self, save = True):
 | 
			
		||||
        """
 | 
			
		||||
        Update the parent object designed by Relation.mapping.thread if the
 | 
			
		||||
        type matches to the one related of the current instance's thread.
 | 
			
		||||
 | 
			
		||||
        If there is no thread assigned to self, set it to the parent of the
 | 
			
		||||
        related object.
 | 
			
		||||
        """
 | 
			
		||||
        relation = self._relation
 | 
			
		||||
        print(relation.__dict__)
 | 
			
		||||
        thread_model = relation.thread_model
 | 
			
		||||
        if not thread_model:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # self.related.parent -> self.thread
 | 
			
		||||
        rel_parent = relation.mapping.get('thread')
 | 
			
		||||
        if not self.thread:
 | 
			
		||||
            rel_parent = getattr(self.related, rel_parent)
 | 
			
		||||
            thread = thread_model.objects.filter(related = rel_parent)
 | 
			
		||||
            if thread.count():
 | 
			
		||||
                self.thread = thread[0]
 | 
			
		||||
                if save:
 | 
			
		||||
                    self.save()
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        # self.thread -> self.related.parent
 | 
			
		||||
        if thread_model is not self.thread_type.model_class():
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        setattr(self.related, rel_parent, self.thread.related)
 | 
			
		||||
        if save:
 | 
			
		||||
            self.save()
 | 
			
		||||
 | 
			
		||||
    def update_mapping (self):
 | 
			
		||||
        relation = self._relation
 | 
			
		||||
        mapping = relation.mapping
 | 
			
		||||
        if not mapping:
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        related = self.related
 | 
			
		||||
        related.__dict__.update({
 | 
			
		||||
            rel_attr: self.__dict__[attr]
 | 
			
		||||
            for attr, rel_attr in mapping.items()
 | 
			
		||||
            if attr is not 'thread' and attr in self.__dict__
 | 
			
		||||
        })
 | 
			
		||||
 | 
			
		||||
        self.update_thread_mapping(save = False)
 | 
			
		||||
        related.save()
 | 
			
		||||
 | 
			
		||||
    def save (self, *args, **kwargs):
 | 
			
		||||
        if not self.title and self.related:
 | 
			
		||||
            self.title = self.get_attribute('title')
 | 
			
		||||
 | 
			
		||||
        if self._relation.bind_mapping:
 | 
			
		||||
            self.related.__dict__.update({
 | 
			
		||||
                rel_attr: self.__dict__[attr]
 | 
			
		||||
                for attr, rel_attr in self.Relation.mapping.items()
 | 
			
		||||
            })
 | 
			
		||||
            self.related.save()
 | 
			
		||||
 | 
			
		||||
        self.update_mapping()
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user