Skip to content

Commit

Permalink
Add delete function.
Browse files Browse the repository at this point in the history
It could be implemented with the ORM. However, we have everything needed
to implement it with raw SQL; it's simpler, especially when it comes to
exception handling; and there's no need to pull the big guns here.
  • Loading branch information
aaugustin committed Dec 16, 2023
1 parent bfdfd63 commit 14fe696
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 9 deletions.
53 changes: 49 additions & 4 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,40 @@ The complete signature of ``get_last_value`` is:
sequence generated but it makes no guarantees.** Concurrent calls to
``get_next_value`` may produce unexpected results of ``get_last_value``.

``delete``
----------

.. code:: pycon
>>> from sequences import delete
This function deletes a sequence. It returns ``True`` if the sequence existed
and was deleted and ``False`` otherwise.

.. code:: pycon
>>> company_id = "b1f6cdef-367f-49e4-9cf5-bb0d34707af8"
>>> get_next_value(f"invoices—{company_id}")
1
>>> delete(f"invoices—{company_id}")
True
>>> delete(f"invoices—{company_id}")
False
It accepts ``using="..."`` for selecting the database, like ``get_next_value``.

The complete signature of ``delete`` is:

.. code:: python
delete(
sequence_name="default",
*,
using=None,
)
``delete`` is useful when you create many sequences and want to dispose of some.

``Sequence``
------------

Expand All @@ -253,8 +287,8 @@ sequence generated but it makes no guarantees.** Concurrent calls to
(not to be confused with ``sequences.models.Sequence``, a private API)

This class stores parameters for a sequence and provides ``get_next_value``
and ``get_last_value`` methods:
This class stores parameters for a sequence and provides ``get_next_value``,
``get_last_value``, and ``delete`` methods:

.. code:: pycon
Expand All @@ -265,6 +299,8 @@ and ``get_last_value`` methods:
2
>>> claim_ids.get_last_value()
2
>>> claim_ids.delete()
True
This reduces the risk of errors when the same sequence is used in multiple
places.
Expand Down Expand Up @@ -300,8 +336,12 @@ The complete API is:
self,
)
All parameters have the same meaning as in the ``get_next_value`` and
``get_last_value`` functions.
Sequence.delete(
self,
)
All parameters have the same meaning as in the ``get_next_value``,
``get_last_value``, and ``delete`` functions.

Examples
========
Expand Down Expand Up @@ -458,6 +498,11 @@ Build and publish the new version:
Changelog
=========

2.9
---

* Add the ``delete`` function.

2.8
---

Expand Down
46 changes: 44 additions & 2 deletions src/sequences/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,20 @@
UPDATE last = {db_table}.last + 1
"""

DELETE = """
DELETE
FROM {db_table}
WHERE name = %s
"""


def get_last_value(
sequence_name="default",
*,
using=None,
):
"""
Return the last value for a given sequence.
Return the last value for a sequence.
"""
# Inner import because models cannot be imported before their application.
Expand Down Expand Up @@ -59,7 +65,7 @@ def get_next_value(
using=None,
):
"""
Return the next value for a given sequence.
Return the next value for a sequence.
"""
# Inner import because models cannot be imported before their application.
Expand Down Expand Up @@ -124,6 +130,32 @@ def get_next_value(
return sequence.last


def delete(
sequence_name="default",
*,
using=None,
):
"""
Delete a sequence.
"""
# Inner import because models cannot be imported before their application.
from .models import Sequence

if using is None:
using = router.db_for_write(Sequence)

connection = connections[using]
db_table = connection.ops.quote_name(Sequence._meta.db_table)

with connection.cursor() as cursor:
cursor.execute(
DELETE.format(db_table=db_table),
[sequence_name],
)
return bool(cursor.rowcount)


class Sequence:
"""
Generate a gapless sequence of integer values.
Expand Down Expand Up @@ -168,6 +200,16 @@ def get_next_value(self, *, nowait=False):
using=self.using,
)

def delete(self):
"""
Delete the sequence.
"""
return delete(
self.sequence_name,
using=self.using,
)

def __iter__(self):
return self

Expand Down
16 changes: 13 additions & 3 deletions tests/test_sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from django.db import DatabaseError, connection, transaction
from django.test import TestCase, TransactionTestCase, skipUnlessDBFeature

from sequences import Sequence, get_last_value, get_next_value
from sequences import Sequence, delete, get_last_value, get_next_value


class SingleConnectionTestsMixin:
Expand All @@ -16,7 +16,10 @@ def test_functions_defaults(self):
self.assertEqual(get_last_value(), 1)
self.assertEqual(get_next_value(), 2)
self.assertEqual(get_last_value(), 2)
self.assertEqual(get_next_value(), 3)
self.assertEqual(delete(), True)
self.assertEqual(get_last_value(), None)
self.assertEqual(delete(), False)
self.assertEqual(get_last_value(), None)

def test_functions_sequence_name(self):
self.assertEqual(get_last_value("cases"), None)
Expand Down Expand Up @@ -55,7 +58,10 @@ def test_class_defaults(self):
self.assertEqual(seq.get_last_value(), 1)
self.assertEqual(seq.get_next_value(), 2)
self.assertEqual(seq.get_last_value(), 2)
self.assertEqual(seq.get_next_value(), 3)
self.assertEqual(seq.delete(), True)
self.assertEqual(seq.get_last_value(), None)
self.assertEqual(seq.delete(), False)
self.assertEqual(seq.get_last_value(), None)

def test_class_iter(self):
seq = Sequence(initial_value=0)
Expand All @@ -77,6 +83,10 @@ def test_class_sequence_name(self):
self.assertEqual(invoices_seq.get_next_value(), 1)
self.assertEqual(invoices_seq.get_last_value(), 1)
self.assertEqual(invoices_seq.get_next_value(), 2)
self.assertEqual(cases_seq.delete(), True)
self.assertEqual(cases_seq.get_last_value(), None)
self.assertEqual(invoices_seq.delete(), True)
self.assertEqual(invoices_seq.get_last_value(), None)

def test_class_initial_value(self):
customers_seq = Sequence("customers", initial_value=1000)
Expand Down

0 comments on commit 14fe696

Please sign in to comment.