forked from rc/aircox
move files
This commit is contained in:
236
cms/models.py
Normal file
236
cms/models.py
Normal file
@ -0,0 +1,236 @@
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.contenttypes.fields import GenericForeignKey
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
from django.utils import timezone
|
||||
from django.utils.text import slugify
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from django.db.models.signals import post_init, post_save, post_delete
|
||||
from django.dispatch import receiver
|
||||
|
||||
from taggit.managers import TaggableManager
|
||||
|
||||
|
||||
class Post (models.Model):
|
||||
thread_type = models.ForeignKey(
|
||||
ContentType,
|
||||
on_delete=models.SET_NULL,
|
||||
blank = True, null = True
|
||||
)
|
||||
thread_pk = models.PositiveIntegerField(
|
||||
blank = True, null = True
|
||||
)
|
||||
thread = GenericForeignKey('thread_type', 'thread_pk')
|
||||
|
||||
author = models.ForeignKey(
|
||||
User,
|
||||
verbose_name = _('author'),
|
||||
blank = True, null = True,
|
||||
)
|
||||
date = models.DateTimeField(
|
||||
_('date'),
|
||||
default = timezone.datetime.now
|
||||
)
|
||||
published = models.BooleanField(
|
||||
verbose_name = _('public'),
|
||||
default = True
|
||||
)
|
||||
|
||||
title = models.CharField (
|
||||
_('title'),
|
||||
max_length = 128,
|
||||
)
|
||||
content = models.TextField (
|
||||
_('description'),
|
||||
blank = True, null = True
|
||||
)
|
||||
image = models.ImageField(
|
||||
blank = True, null = True
|
||||
)
|
||||
tags = TaggableManager(
|
||||
_('tags'),
|
||||
blank = True,
|
||||
)
|
||||
|
||||
def detail_url (self):
|
||||
return reverse(self._meta.verbose_name.lower() + '_detail',
|
||||
kwargs = { 'pk': self.pk,
|
||||
'slug': slugify(self.title) })
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class Article (Post):
|
||||
static_page = models.BooleanField(
|
||||
_('static page'),
|
||||
default = False,
|
||||
)
|
||||
focus = models.BooleanField(
|
||||
_('article is focus'),
|
||||
default = False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Article')
|
||||
verbose_name_plural = _('Articles')
|
||||
|
||||
|
||||
class RelatedPostBase (models.base.ModelBase):
|
||||
"""
|
||||
Metaclass for RelatedPost children.
|
||||
"""
|
||||
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 {}
|
||||
|
||||
related_model = rel.get('model')
|
||||
if related_model:
|
||||
attrs['related'] = models.ForeignKey(related_model)
|
||||
|
||||
if not '__str__' in attrs:
|
||||
attrs['__str__'] = lambda self: str(self.related)
|
||||
|
||||
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
|
||||
|
||||
model = super().__new__(cl, name, bases, attrs)
|
||||
cl.register(related_model, model)
|
||||
return model
|
||||
|
||||
|
||||
class RelatedPost (Post, metaclass = RelatedPostBase):
|
||||
"""
|
||||
Use this post to generate Posts that are related to an external model. An
|
||||
extra field "related" will be generated, and some bindings are possible to
|
||||
update te related object on save if desired;
|
||||
|
||||
This is done through a class name Relation inside the declaration of the new
|
||||
model.
|
||||
"""
|
||||
related = None
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
class Relation:
|
||||
"""
|
||||
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 (in order to
|
||||
establish a parent-child relation between two models)
|
||||
* thread_model: generated by the metaclass that point to the
|
||||
RelatedModel class related to the model that is the parent of
|
||||
the current related one.
|
||||
"""
|
||||
model = None
|
||||
mapping = None # values to map { post_attr: rel_attr }
|
||||
thread = None
|
||||
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
|
||||
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')
|
||||
|
||||
self.update_mapping()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
|
Reference in New Issue
Block a user