From d634a949020e99adcd6ccc178e6ced2767ad6e6b Mon Sep 17 00:00:00 2001 From: Lubos Matl Date: Mon, 21 Sep 2020 10:32:36 +0200 Subject: [PATCH] Added SmartModel default __str__ method and add ordering to first/last queryset method --- chamber/models/__init__.py | 34 ++++++++++++++++--- docs/models.rst | 4 +++ .../test_chamber/tests/models/__init__.py | 14 ++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/chamber/models/__init__.py b/chamber/models/__init__.py index 4d59305..6a6cf8a 100644 --- a/chamber/models/__init__.py +++ b/chamber/models/__init__.py @@ -6,6 +6,7 @@ import django from django.db import models, transaction, OperationalError +from django.db.models.manager import BaseManager from django.db.models.base import ModelBase from django.utils.translation import ugettext_lazy as _ from django.utils.functional import cached_property @@ -264,11 +265,33 @@ def change_and_save(self, update_only_changed_fields=False, **changed_fields): bulk_change_and_save(self, update_only_changed_fields=update_only_changed_fields, **changed_fields) return self.filter() + def first(self, *field_names): + """ + Adds possibility to set order fields to default Django first method. + """ + if field_names: + return self.order_by(*field_names).first() + else: + return super().first() + + def last(self, *field_names): + """ + Adds possibility to set order fields to default Django last method. + """ + if field_names: + return self.order_by(*field_names).last() + else: + return super().last() + class SmartQuerySet(SmartQuerySetMixin, models.QuerySet): pass +class SmartManager(BaseManager.from_queryset(SmartQuerySet)): + pass + + class SmartModelBase(ModelBase): """ Smart model meta class that register dispatchers to the post or pre save signals. @@ -284,7 +307,7 @@ def __new__(cls, name, bases, attrs): class SmartModel(AuditModel, metaclass=SmartModelBase): - objects = SmartQuerySet.as_manager() + objects = SmartManager() dispatchers = [] @@ -295,6 +318,12 @@ def __init__(self, *args, **kwargs): self.changed_fields = DynamicChangedFields(self) self.post_save = Signal(self) + class Meta: + abstract = True + + def __str__(self): + return '{} #{}'.format(self._meta.verbose_name, self.pk) + @classmethod def from_db(cls, db, field_names, values): new = super().from_db(db, field_names, values) @@ -529,9 +558,6 @@ def get_locked_instance(self): return self.__class__.objects.filter(pk=self.pk).select_for_update().get() - class Meta: - abstract = True - class SmartOptions(Options): diff --git a/docs/models.rst b/docs/models.rst index 17ea0f4..fc4f7f0 100644 --- a/docs/models.rst +++ b/docs/models.rst @@ -284,6 +284,10 @@ will assume the custom filters to be there). Changes selected fields on the selected queryset and saves it and returns changed objects in the queryset. Difference from update is that there is called save method on the instance, but it is slower. If you want to update only changed fields in the database you can use parameter ``update_only_changed_fields`` to achieve it + .. method:: first(**field_names) + Method is only shortcut to ``Model.objects.order_by('field_name').first()``. With this method you can use ``Model.objects.first('field_name')`` + .. method:: last(**field_names) + Method is only shortcut to ``Model.objects.order_by('field_name').last()``. With this method you can use ``Model.objects.last('field_name')`` diff --git a/example/dj/apps/test_chamber/tests/models/__init__.py b/example/dj/apps/test_chamber/tests/models/__init__.py index 100a49e..29bd7fe 100644 --- a/example/dj/apps/test_chamber/tests/models/__init__.py +++ b/example/dj/apps/test_chamber/tests/models/__init__.py @@ -317,3 +317,17 @@ def test_smart_queryset_fast_distinct(self): assert_equal(qs.count(), 2) assert_equal(tuple(qs.values_list('pk', flat=True)), (t.pk, t.pk)) assert_equal(qs.fast_distinct().count(), 1) + + def test_smart_model_first_and_last_with_order(self): + test3 = TestSmartModel.objects.create(name='3') + test2 = TestSmartModel.objects.create(name='2') + test1 = TestSmartModel.objects.create(name='1') + + assert_equal(TestSmartModel.objects.first('id'), test3) + assert_equal(TestSmartModel.objects.last('id'), test1) + + assert_equal(TestSmartModel.objects.first('name'), test1) + assert_equal(TestSmartModel.objects.last('name'), test3) + + assert_equal(TestSmartModel.objects.filter(name='2').first(), test2) + assert_equal(TestSmartModel.objects.filter(name='2').last(), test2)