RelatedPost: mapping['thread'] now manage relation between thread and parent of related object

This commit is contained in:
bkfox
2015-10-13 12:38:18 +02:00
parent cde58334bd
commit 0032e216b8
6 changed files with 200 additions and 111 deletions

View File

@ -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)