forked from rc/aircox
logo; RelatedPost sync with related object
This commit is contained in:
@ -92,15 +92,15 @@ class RelatedPostBase (models.base.ModelBase):
|
||||
registry = {}
|
||||
|
||||
@classmethod
|
||||
def register (cl, key, model):
|
||||
def register (cl, key, post_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:
|
||||
if key in cl.registry and cl.registry[key] is not post_model:
|
||||
raise ValueError('A model has yet been registered with "{}"'
|
||||
.format(key))
|
||||
cl.registry[key] = model
|
||||
cl.registry[key] = post_model
|
||||
return key
|
||||
|
||||
@classmethod
|
||||
@ -137,13 +137,15 @@ class RelatedPostBase (models.base.ModelBase):
|
||||
return rel
|
||||
|
||||
def __new__ (cl, name, bases, attrs):
|
||||
# TODO: allow proxy models and better inheritance
|
||||
if name == 'RelatedPost':
|
||||
return super().__new__(cl, name, bases, attrs)
|
||||
|
||||
rel = cl.make_relation(name, attrs)
|
||||
field_args = rel.field_args or {}
|
||||
attrs['_relation'] = rel
|
||||
attrs.update({ x:y for x,y in {
|
||||
'related': models.ForeignKey(rel.model),
|
||||
'related': models.ForeignKey(rel.model, **field_args),
|
||||
'__str__': lambda self: str(self.related)
|
||||
}.items() if not attrs.get(x) })
|
||||
|
||||
@ -207,6 +209,8 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
* rel_to_post: auto update the post when related object is updated
|
||||
* thread_model: generated by the metaclass, points to the RelatedPost
|
||||
model generated for the bindings.thread object.
|
||||
* field_args: dict of arguments to pass to the ForeignKey constructor,
|
||||
such as: ForeignKey(related_model, **field_args)
|
||||
|
||||
Be careful with post_to_rel!
|
||||
* There is no check of permissions when related object is synchronised
|
||||
@ -217,14 +221,15 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
model = None
|
||||
bindings = None # values to map { post_attr: rel_attr }
|
||||
post_to_rel = False
|
||||
rel_to_post = True
|
||||
rel_to_post = False
|
||||
thread_model = None
|
||||
field_args = None
|
||||
|
||||
def get_rel_attr(self, attr):
|
||||
attr = self._relation.bindings.get(attr)
|
||||
return getattr(self.related, attr) if attr else None
|
||||
|
||||
def set_rel_attr(self, attr, value)
|
||||
def set_rel_attr(self, attr, value):
|
||||
if attr not in self._relation.bindings:
|
||||
raise AttributeError('attribute {} is not bound'.format(attr))
|
||||
attr = self._relation.bindings.get(attr)
|
||||
@ -240,13 +245,13 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
if not rel.bindings:
|
||||
return
|
||||
|
||||
for attr, rel_attr in rel.bindings.items()
|
||||
for attr, rel_attr in rel.bindings.items():
|
||||
if attr == 'thread':
|
||||
continue
|
||||
value = getattr(self, attr) if hasattr(self, attr) else None
|
||||
setattr(self.related, rel_attr, value)
|
||||
|
||||
if self.thread_model:
|
||||
if rel.thread_model:
|
||||
thread = self.thread if not issubclass(thread, rel.thread_model) \
|
||||
else None
|
||||
self.set_rel_attr('thread', thread.related)
|
||||
@ -254,6 +259,19 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
if save:
|
||||
self.related.save()
|
||||
|
||||
|
||||
@classmethod
|
||||
def sync_from_rel(cl, rel, save = True):
|
||||
"""
|
||||
Update a rel_to_post from a given rel object. Return -1 if there is no
|
||||
related post to update
|
||||
"""
|
||||
self = cl.objects.filter(related = rel)
|
||||
if not self or not self.count():
|
||||
return -1
|
||||
self[0].rel_to_post(save)
|
||||
|
||||
|
||||
def rel_to_post(self, save = True):
|
||||
"""
|
||||
Change the post using the related object bound values. Save the
|
||||
@ -264,7 +282,7 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
if rel.bindings:
|
||||
return
|
||||
|
||||
for attr, rel_attr in rel.bindings.items()
|
||||
for attr, rel_attr in rel.bindings.items():
|
||||
if attr == 'thread':
|
||||
continue
|
||||
self.set_rel_attr
|
||||
@ -272,7 +290,7 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
if hasattr(self.related, attr) else None
|
||||
setattr(self, attr, value)
|
||||
|
||||
if self.thread_model:
|
||||
if rel.thread_model:
|
||||
thread = self.get_rel_attr('thread')
|
||||
thread = rel.thread_model.objects.filter(related = thread) \
|
||||
if thread else None
|
||||
@ -282,11 +300,19 @@ class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
if save:
|
||||
self.save()
|
||||
|
||||
def __init__ (self, *kargs, **kwargs):
|
||||
super().__init__(*kargs, **kwargs)
|
||||
# we use this method for sync, in order to avoid intrusive code on other
|
||||
# applications, e.g. using signals.
|
||||
if self._relation.rel_to_post:
|
||||
self.rel_to_post(save = False)
|
||||
|
||||
def save (self, *args, **kwargs):
|
||||
# TODO handle when related change
|
||||
if not self.title and self.related:
|
||||
self.title = self.get_rel_attr('title')
|
||||
if self._relation.post_to_rel:
|
||||
self.post_to_rel(False)
|
||||
self.post_to_rel(save = True)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
<meta name="description" content="{{ website.description }}">
|
||||
<meta name="keywords" content="{{ website.tags }}">
|
||||
|
||||
<link rel="stylesheet" href="{% static "aircox_cms/styles.css" %}" type="text/css">
|
||||
<link rel="stylesheet" href="{% static "aircox/cms/styles.css" %}" type="text/css">
|
||||
{% if website.styles %}
|
||||
<link rel="stylesheet" href="{% static website.styles %}" type="text/css">
|
||||
{% endif %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends embed|yesno:"aircox_cms/base_content.html,aircox_cms/base_site.html" %}
|
||||
{% extends embed|yesno:"aircox/cms/base_content.html,aircox/cms/base_site.html" %}
|
||||
|
||||
{% block title %}
|
||||
{{ object.title }}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends embed|yesno:"aircox_cms/base_content.html,aircox_cms/base_site.html" %}
|
||||
{% extends embed|yesno:"aircox/cms/base_content.html,aircox/cms/base_site.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load thumbnail %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "aircox_cms/base_section.html" %}
|
||||
{% extends "aircox/cms/base_section.html" %}
|
||||
|
||||
{% block content %}
|
||||
{% if title %}
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% extends "aircox_cms/section.html" %}
|
||||
{% extends "aircox/cms/section.html" %}
|
||||
|
||||
{% load thumbnail %}
|
||||
|
||||
|
@ -307,6 +307,8 @@ class Sections:
|
||||
"""
|
||||
@property
|
||||
def content (self):
|
||||
if not self.object.image:
|
||||
return ''
|
||||
return '<img src="{}" class="post_image">'.format(
|
||||
self.object.image.url
|
||||
)
|
||||
|
Reference in New Issue
Block a user