diff options
author | luxagraf <sng@luxagraf.net> | 2018-03-13 11:19:31 -0500 |
---|---|---|
committer | luxagraf <sng@luxagraf.net> | 2018-03-13 11:19:31 -0500 |
commit | ab62d6af3ed33234f0ab849ef5d5c3b5a400f6e3 (patch) | |
tree | b4aa8932a4dffeaba89dfaa74e0d0fae61785ec2 /app | |
parent | ce9c1a25a2872979b969c64f804529d200a8c0ad (diff) |
fixed an ordering bug in sightings AP
Diffstat (limited to 'app')
-rw-r--r-- | app/sightings/models.py | 63 | ||||
-rw-r--r-- | app/utils/next_prev.py | 80 |
2 files changed, 129 insertions, 14 deletions
diff --git a/app/sightings/models.py b/app/sightings/models.py index 80b9ba9..8531772 100644 --- a/app/sightings/models.py +++ b/app/sightings/models.py @@ -1,15 +1,25 @@ import datetime +import re from django.urls import reverse from django.apps import apps from django.template.defaultfilters import slugify from django.contrib.gis.db import models -from django.contrib.auth.models import User +# from django.contrib.auth.models import User from django.utils import timezone -from locations.models import Location from django import forms from django.conf import settings -from photos.models import LuxImage +# from photos.models import LuxImage + +from locations.models import Location +from utils.widgets import parse_image +from utils.widgets import markdown_to_html +from utils.next_prev import next_in_order, prev_in_order + + +def render_images(s): + s = re.sub('<img(.*)/>', parse_image, s) + return s def get_upload_path(self, filename): @@ -48,6 +58,7 @@ class APClass(models.Model): def __str__(self): return self.common_name + class AP(models.Model): common_name = models.CharField(max_length=200) slug = models.SlugField() @@ -56,8 +67,13 @@ class AP(models.Model): apclass = models.ForeignKey(APClass, on_delete=models.CASCADE) body_html = models.TextField(null=True, blank=True) body_markdown = models.TextField(null=True, blank=True) - #image = models.FileField(upload_to=get_upload_path, null=True, blank=True, help_text="width of high res is 1360px") - #image_credit = models.CharField(max_length=200, blank=True, null=True) + # image = models.FileField(upload_to=get_upload_path, null=True, blank=True, help_text="width of high res is 1360px") + # image_credit = models.CharField(max_length=200, blank=True, null=True) + + class Meta: + verbose_name_plural = 'Animal/Plant' + verbose_name = 'Animal/Plant' + ordering = ["common_name", ] def __str__(self): return self.common_name @@ -71,10 +87,29 @@ class AP(models.Model): def kind(self): return self.apclass.kind - class Meta: - verbose_name_plural = 'Animal/Plant' - verbose_name = 'Animal/Plant' - ordering = ["common_name", ] + def get_prev(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + return prev_in_order(self, model.objects.all()) + + def get_next(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + return next_in_order(self, model.objects.all()) + + @property + def get_previous_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_prev().pk]) + except model.DoesNotExist: + return '' + + @property + def get_next_admin_url(self): + model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) + try: + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next().pk]) + except model.DoesNotExist: + return '' def save(self, *args, **kwargs): if self.pk: @@ -89,9 +124,9 @@ class Sighting(models.Model): point = models.PointField(blank=True) location = models.ForeignKey(Location, on_delete=models.CASCADE, blank=True) pub_date = models.DateTimeField('Date', default=timezone.now) - #seen_by = models.ManyToManyField(User) - #images = models.ManyToManyField(LuxImage, blank=True) - #audio = models.ManyToManyField(BirdAudio, blank=True) + # seen_by = models.ManyToManyField(User) + # images = models.ManyToManyField(LuxImage, blank=True) + # audio = models.ManyToManyField(BirdAudio, blank=True) class Meta: ordering = ["-pub_date", ] @@ -122,13 +157,13 @@ class Sighting(models.Model): @property def get_previous_admin_url(self): n = self.get_previous_by_pub_date() - return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[n.id] ) + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[n.id]) @property def get_next_admin_url(self): model = apps.get_model(app_label=self._meta.app_label, model_name=self._meta.model_name) try: - return reverse('admin:%s_%s_change' %(self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk] ) + return reverse('admin:%s_%s_change' % (self._meta.app_label, self._meta.model_name), args=[self.get_next_by_pub_date().pk]) except model.DoesNotExist: return '' diff --git a/app/utils/next_prev.py b/app/utils/next_prev.py new file mode 100644 index 0000000..766add1 --- /dev/null +++ b/app/utils/next_prev.py @@ -0,0 +1,80 @@ +# -*- coding: utf-8 -*- +# from https://github.com/gregplaysguitar/django-next-prev/blob/master/next_prev.py + +from functools import partial + +from django.db import models + +if not locals().get('reduce'): + from functools import reduce + +__version__ = '1.0.1' +VERSION = tuple(map(int, __version__.split('.'))) + + +def get_model_attr(instance, attr): + """Example usage: get_model_attr(instance, 'category__slug')""" + for field in attr.split('__'): + instance = getattr(instance, field) + return instance + + +def next_or_prev_in_order(instance, qs=None, prev=False, loop=False): + """Get the next (or previous with prev=True) item for instance, from the + given queryset (which is assumed to contain instance) respecting + queryset ordering. If loop is True, return the first/last item when the + end/start is reached. """ + + if not qs: + qs = instance.__class__.objects.all() + + if prev: + qs = qs.reverse() + lookup = 'lt' + else: + lookup = 'gt' + + q_list = [] + prev_fields = [] + + if qs.query.extra_order_by: + ordering = qs.query.extra_order_by + elif qs.query.order_by: + ordering = qs.query.order_by + elif qs.query.get_meta().ordering: + ordering = qs.query.get_meta().ordering + else: + ordering = [] + + ordering = list(ordering) + + # if the ordering doesn't contain pk, append it and reorder the queryset + # to ensure consistency + if 'pk' not in ordering and '-pk' not in ordering: + ordering.append('pk') + qs = qs.order_by(*ordering) + + for field in ordering: + if field[0] == '-': + this_lookup = (lookup == 'gt' and 'lt' or 'gt') + field = field[1:] + else: + this_lookup = lookup + q_kwargs = dict([(f, get_model_attr(instance, f)) + for f in prev_fields]) + key = "%s__%s" % (field, this_lookup) + q_kwargs[key] = get_model_attr(instance, field) + q_list.append(models.Q(**q_kwargs)) + prev_fields.append(field) + try: + return qs.filter(reduce(models.Q.__or__, q_list))[0] + except IndexError: + length = qs.count() + if loop and length > 1: + # queryset is reversed above if prev + return qs[0] + return None + + +next_in_order = partial(next_or_prev_in_order, prev=False) +prev_in_order = partial(next_or_prev_in_order, prev=True) |