Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions docs/developer/other-utilities.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,12 @@ Which use respectively ``AutoCreatedField``, ``AutoLastModifiedField``
from ``model_utils.fields`` (self-updating fields providing the creation
date-time and the last modified date-time).

By default, querysets are ordered by newest creation time and then by
primary key, using ``("-created", "-pk")``. A subclass defining its own
``Meta`` class must inherit ``TimeStampedEditableModel.Meta`` to preserve
this ordering. Explicit model ordering and ``QuerySet.order_by()`` calls
override it.

REST API Utilities
------------------

Expand Down
1 change: 1 addition & 0 deletions openwisp_utils/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ class TimeStampedEditableModel(UUIDModel):

class Meta:
abstract = True
ordering = ("-created", "-pk")
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 5.2.13 on 2026-08-03 20:58

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("metric_collection", "0001_initial"),
]

operations = [
migrations.AlterModelOptions(
name="consent",
options={"ordering": ("-created", "-pk")},
),
]
20 changes: 19 additions & 1 deletion openwisp_utils/metric_collection/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from unittest.mock import patch
from uuid import UUID

import requests
from django.apps import apps
Expand All @@ -22,6 +23,23 @@


class TestOpenwispVersion(TestCase):
def test_consent_ordering(self):
old = Consent.objects.create(id=UUID("00000000-0000-0000-0000-000000000001"))
tied = Consent.objects.create(id=UUID("00000000-0000-0000-0000-000000000002"))
newest = Consent.objects.create(id=UUID("00000000-0000-0000-0000-000000000003"))
created = datetime(2026, 1, 1, tzinfo=timezone.utc)
Consent.objects.filter(pk__in=[old.pk, tied.pk]).update(created=created)
Consent.objects.filter(pk=newest.pk).update(created=created + timedelta(days=1))
self.assertEqual(Consent._meta.ordering, ("-created", "-pk"))
self.assertEqual(
list(Consent.objects.values_list("pk", flat=True)),
[newest.pk, tied.pk, old.pk],
)
self.assertEqual(
list(Consent.objects.order_by("pk").values_list("pk", flat=True)),
[old.pk, tied.pk, newest.pk],
)

Comment thread
coderabbitai[bot] marked this conversation as resolved.
def setUp(self):
# The post_migrate signal creates the first OpenwispVersion object
# and uses the actual modules installed in the Python environment.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Generated by Django 5.2.13 on 2026-08-03 20:58

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
("test_project", "0009_shelf_writers"),
]

operations = [
migrations.AlterModelOptions(
name="book",
options={"ordering": ("name", "pk")},
),
migrations.AlterModelOptions(
name="shelf",
options={"ordering": ("-created", "-pk")},
),
]
5 changes: 3 additions & 2 deletions tests/test_project/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ class Shelf(TimeStampedEditableModel):
def __str__(self):
return self.name

class Meta:
class Meta(TimeStampedEditableModel.Meta):
abstract = False

def clean(self):
Expand All @@ -74,8 +74,9 @@ class Book(TimeStampedEditableModel):
def __str__(self):
return self.name

class Meta:
class Meta(TimeStampedEditableModel.Meta):
abstract = False
ordering = ("name", "pk")


class RadiusAccounting(models.Model):
Expand Down
56 changes: 56 additions & 0 deletions tests/test_project/tests/test_model.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
from datetime import datetime, timedelta, timezone
from unittest.mock import patch
from uuid import UUID

from django.core.exceptions import ValidationError
from django.db import connection, models
Expand All @@ -13,6 +15,60 @@
class TestModel(TestCase):
TEST_KEY = "w1gwJxKaHcamUw62TQIPgYchwLKn3AA0"

def test_timestamped_model_ordering(self):
old_shelf = Shelf.objects.create(
id=UUID("00000000-0000-0000-0000-000000000001"), name="Old shelf"
)
tied_shelf = Shelf.objects.create(
id=UUID("00000000-0000-0000-0000-000000000002"), name="Tied shelf"
)
newest_shelf = Shelf.objects.create(
id=UUID("00000000-0000-0000-0000-000000000003"), name="Newest shelf"
)
old_book = Book.objects.create(
id=UUID("00000000-0000-0000-0000-000000000004"),
name="Same book",
author="Author",
shelf=old_shelf,
)
tied_book = Book.objects.create(
id=UUID("00000000-0000-0000-0000-000000000005"),
name="Same book",
author="Author",
shelf=tied_shelf,
)
newest_book = Book.objects.create(
id=UUID("00000000-0000-0000-0000-000000000006"),
name="Other book",
author="Author",
shelf=newest_shelf,
)
created = datetime(2026, 1, 1, tzinfo=timezone.utc)
Shelf.objects.filter(pk__in=[old_shelf.pk, tied_shelf.pk]).update(
created=created
)
Shelf.objects.filter(pk=newest_shelf.pk).update(
created=created + timedelta(days=1)
)
Book.objects.filter(pk__in=[old_book.pk, tied_book.pk]).update(created=created)
Book.objects.filter(pk=newest_book.pk).update(
created=created + timedelta(days=1)
)
self.assertEqual(Shelf._meta.ordering, ("-created", "-pk"))
self.assertEqual(Book._meta.ordering, ("name", "pk"))
self.assertEqual(
list(Shelf.objects.values_list("pk", flat=True)),
[newest_shelf.pk, tied_shelf.pk, old_shelf.pk],
)
self.assertEqual(
list(Book.objects.values_list("pk", flat=True)),
[newest_book.pk, old_book.pk, tied_book.pk],
)
self.assertEqual(
list(Shelf.objects.order_by("name").values_list("pk", flat=True)),
[newest_shelf.pk, old_shelf.pk, tied_shelf.pk],
)
Comment thread
coderabbitai[bot] marked this conversation as resolved.

def test_key_validator(self):
p = Project.objects.create(name="test_project")
p.key = "key/key"
Expand Down
5 changes: 4 additions & 1 deletion tests/test_project/tests/test_selenium.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,7 +652,10 @@ def test_autocomplete_shelf_filter(self):
select_id = "id-shelf__id-dal-filter"
filter_css_selector = f"#select2-{select_id}-container"
filter_options = f'//*[@id="select2-{select_id}-results"]/li'
filter_option_xpath = f'//*[@id="select2-{select_id}-results"]/li[2]'
filter_option_xpath = (
f'//*[@id="select2-{select_id}-results"]/li'
f'[contains(text(), "{factual_shelf.name}")]'
)

result_xpath = '//*[@id="result_list"]/tbody/tr/th/a[contains(text(), "{}")]'
self.open(url)
Expand Down
Loading