diff --git a/cms/decorators.py b/cms/decorators.py index 62ccc59..c86cf9d 100644 --- a/cms/decorators.py +++ b/cms/decorators.py @@ -54,7 +54,7 @@ class Exposure: 'exp': cl._exposure, }) res = render_to_string(exp.template_name, - v, request = request) + ctx, request = request) return HttpResponse(res or '') # id = str(uuid.uuid1()) diff --git a/cms/models.py b/cms/models.py index 6ded6dc..ed8148c 100644 --- a/cms/models.py +++ b/cms/models.py @@ -2,7 +2,7 @@ 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 import timezone as tz from django.utils.text import slugify from django.utils.translation import ugettext as _, ugettext_lazy @@ -75,7 +75,7 @@ class Comment(models.Model, Routable): ) date = models.DateTimeField( _('date'), - default = timezone.datetime.now + auto_now_add = True, ) content = models.TextField ( _('comment'), @@ -107,10 +107,18 @@ class Post (models.Model, Routable): You can declare an extra property "info" that can be used to append info in lists rendering. """ + # used for inherited children + real_type = models.CharField( + max_length=32, + blank = True, null = True, + ) + # metadata + # FIXME: on_delete thread_type = models.ForeignKey( ContentType, on_delete=models.SET_NULL, + related_name = 'thread_type', blank = True, null = True ) thread_id = models.PositiveIntegerField( @@ -135,7 +143,7 @@ class Post (models.Model, Routable): ) date = models.DateTimeField( _('date'), - default = timezone.datetime.now + default = tz.datetime.now ) title = models.CharField ( _('title'), @@ -154,6 +162,11 @@ class Post (models.Model, Routable): blank = True, ) + info = '' + """ + Used to be extended: used in template to render contextual information about + a sub-post item. + """ search_fields = [ 'title', 'content', 'tags__name' ] """ Fields on which routes.SearchRoute must run the search @@ -196,7 +209,7 @@ class Post (models.Model, Routable): self.content = self.thread.content if not self.image: self.image = self.thread.image - if not self.tags and self.pk: + if self.pk and not self.tags: self.tags = self.thread.tags def get_object_list(self, request, object, **kwargs): @@ -228,17 +241,24 @@ class Post (models.Model, Routable): for tag in self.tags.all() ]) + def downcast(self): + """ + Return a downcasted version of the post if it is from another + model, or itself + """ + if not self.real_type or type(self) != Post: + return self + return getattr(self, self.real_type) + def save(self, make_safe = True, *args, **kwargs): + if type(self) != Post and not self.real_type: + self.real_type = type(self).__name__.lower() + if self.date and tz.is_naive(self.date): + self.date = tz.make_aware(self.date) if make_safe: self.make_safe() - if self.date and self.date.tzinfo is None or \ - self.date.tzinfo.utcoffset(self.date) is None: - timezone.make_aware(self.date) return super().save(*args, **kwargs) - class Meta: - abstract = True - class RelatedMeta (models.base.ModelBase): """ @@ -499,6 +519,8 @@ class RelatedPost (Post, metaclass = RelatedMeta): continue value = rel_attr(self, self.related) if callable(rel_attr) else \ getattr(self.related, rel_attr) + if type(value) == tz.datetime and tz.is_naive(value): + value = tz.make_aware(value) set_attr(attr, value) if rel.thread_model: diff --git a/cms/qcombine.py b/cms/qcombine.py deleted file mode 100644 index d24e840..0000000 --- a/cms/qcombine.py +++ /dev/null @@ -1,153 +0,0 @@ -import operator -import itertools -import heapq - -from django.utils.translation import ugettext as _, ugettext_lazy -from django.db.models.query import QuerySet - - -class QCombine: - """ - This class helps to combine querysets of different models and lists of - object, and to iter over it. - - Notes: - - when working on fields, we assume that they exists on all of them; - - for efficiency, there is no possibility to change order per field; - to do so, do it directly on the querysets - - we dont clone the combinator in order to avoid overhead - """ - order_fields = None - lists = None - - def __init__(self, *lists): - """ - lists: list of querysets that are used to initialize the stuff. - """ - self.lists = list(lists) or [] - - def map(self, qs_func, non_qs = None): - """ - Map results of qs_func for QuerySet instance and of non_qs for - the others (if given), because QuerySet always clones itself. - """ - for i, qs in enumerate(self.lists): - if issubclass(type(qs), QuerySet): - self.lists[i] = qs_func(qs) - elif non_qs: - self.lists[i] = non_qs(qs) - - def all(self): - self.map(lambda qs: qs.all()) - - def filter(self, **kwargs): - self.map(lambda qs: qs.filter(**kwargs)) - return self - - def exclude(self, **kwargs): - self.map(lambda qs: qs.exclude(**kwargs)) - return self - - def distinct(self, **kwargs): - self.map(lambda qs: qs.distinct()) - return self - - def get(self, **kwargs): - self.filter(**kwargs) - it = iter(self) - return next(it) - - def order_by(self, *fields, reverse = False): - """ - Order using these fields. For compatibility, if there is - at least one fields whose name starts with '-', reverse - the order - """ - for i, field in enumerate(fields): - if field[0] == '-': - reverse = True - fields[i] = field[1:] - - self.order_reverse = reverse - self.order_fields = fields - self.map( - lambda qs: qs.order_by(*fields), - lambda qs: sorted( - qs, - qs.sort( - key = operator.attrgetter(*fields), - reverse = reverse - ) - ) - ) - return self - - def clone(self): - """ - Make a clone of the class. Not that lists are copied, non-deeply - """ - return QCombine(*[ - qs.all() if issubclass(type(qs), QuerySet) else qs.copy() - for qs in self.lists - ]) - - def __len__(self): - return sum([len(qs) for qs in self.lists]) - - def __iter__(self): - if not self.order_fields: - return itertools.chain(self.lists) - - # FIXME: need it lazy? - return heapq.merge( - *self.lists, - key = operator.attrgetter(*self.order_fields), - reverse = self.order_reverse - ) - - def __getitem__(self, k): - if type(k) == slice: - it = itertools.islice(iter(self), k.start, k.stop, k.step) - else: - it = itertools.islice(iter(self), k) - return list(it) - - -class Manager(type): - """ - Metaclass used to generate the GenericModel.objects property - """ - models = [] - - @property - def objects(self): - qs = QCombine(*[model.objects.all() for model in self.models]) - return qs - - -class GenericModel(metaclass=Manager): - """ - This class is used to register a route for multiple models to a website. - A QCombine is created with qs for all given models when objects - property is retrieved. - - Note: there no other use-case. - """ - class Meta: - verbose_name = _('publication') - verbose_name_plural = _('publications') - - _meta = Meta() - - def __init__(self, **kwargs): - self.__dict__.update(kwargs) - - @classmethod - def reverse(cl, route, use_default = True, **kwargs): - """ - Reverse a url using a given route for the model - simple wrapper - around cl._website.reverse - """ - return cl._website.reverse(cl, route, use_default, **kwargs) - - diff --git a/cms/routes.py b/cms/routes.py index 69777e1..e356eb4 100644 --- a/cms/routes.py +++ b/cms/routes.py @@ -6,8 +6,6 @@ from django.utils.translation import ugettext as _, ugettext_lazy from taggit.models import Tag -import aircox.cms.qcombine as qcombine - class Route: """ @@ -189,7 +187,8 @@ class SearchRoute(Route): ] @classmethod - def __search(cl, model, q): + def get_queryset(cl, model, request, q = None, **kwargs): + q = request.GET.get('q') or q or '' qs = None for search_field in model.search_fields or []: r = models.Q(**{ search_field + '__icontains': q }) @@ -197,17 +196,6 @@ class SearchRoute(Route): else: qs = r return model.objects.filter(qs).distinct() - - @classmethod - def get_queryset(cl, model, request, q = None, **kwargs): - q = request.GET.get('q') or q or '' - if issubclass(model, qcombine.GenericModel): - models = model.models - return qcombine.QCombine( - *(cl.__search(model, q) for model in models) - ) - return cl.__search(model, q) - @classmethod def get_title(cl, model, request, q = None, **kwargs): return _('Search %(search)s in %(model)s') % { diff --git a/cms/templates/aircox/cms/list_item.html b/cms/templates/aircox/cms/list_item.html index 4706b37..dc584ff 100644 --- a/cms/templates/aircox/cms/list_item.html +++ b/cms/templates/aircox/cms/list_item.html @@ -1,7 +1,8 @@ - {% load i18n %} {% load thumbnail %} +{% load aircox_cms %} +{% with object|downcast as object %}