summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/contrib/postgres/functions.py11
-rw-r--r--docs/ref/contrib/postgres/functions.txt31
-rw-r--r--docs/ref/contrib/postgres/index.txt1
-rw-r--r--docs/ref/models/database-functions.txt10
-rw-r--r--docs/releases/1.9.txt5
-rw-r--r--tests/postgres_tests/migrations/0002_create_test_models.py7
-rw-r--r--tests/postgres_tests/models.py4
-rw-r--r--tests/postgres_tests/test_functions.py28
8 files changed, 96 insertions, 1 deletions
diff --git a/django/contrib/postgres/functions.py b/django/contrib/postgres/functions.py
new file mode 100644
index 0000000000..3184c492fc
--- /dev/null
+++ b/django/contrib/postgres/functions.py
@@ -0,0 +1,11 @@
+from django.db.models import DateTimeField
+from django.db.models.functions import Func
+
+
+class TransactionNow(Func):
+ template = 'CURRENT_TIMESTAMP'
+
+ def __init__(self, output_field=None, **extra):
+ if output_field is None:
+ output_field = DateTimeField()
+ super(TransactionNow, self).__init__(output_field=output_field, **extra)
diff --git a/docs/ref/contrib/postgres/functions.txt b/docs/ref/contrib/postgres/functions.txt
new file mode 100644
index 0000000000..c97af99bfb
--- /dev/null
+++ b/docs/ref/contrib/postgres/functions.txt
@@ -0,0 +1,31 @@
+PostgreSQL specific database functions
+======================================
+
+All of these functions are available from the
+``django.contrib.postgres.functions`` module.
+
+.. currentmodule:: django.contrib.postgres.functions
+
+TransactionNow
+--------------
+
+.. class:: TransactionNow()
+
+.. versionadded:: 1.9
+
+Returns the date and time on the database server that the current transaction
+started. If you are not in a transaction it will return the date and time of
+the current statement. This is a complement to
+:class:`django.db.models.functions.Now`, which returns the date and time of the
+current statement.
+
+Note that only the outermost call to :func:`~django.db.transaction.atomic()`
+sets up a transaction and thus sets the time that ``TransactionNow()`` will
+return; nested calls create savepoints which do not affect the transaction
+time.
+
+Usage example::
+
+ >>> from django.contrib.postgres.functions import TransactionNow
+ >>> Article.objects.filter(published__lte=TransactionNow())
+ [<Article: How to Django>]
diff --git a/docs/ref/contrib/postgres/index.txt b/docs/ref/contrib/postgres/index.txt
index 7ff66d0770..a1536680c7 100644
--- a/docs/ref/contrib/postgres/index.txt
+++ b/docs/ref/contrib/postgres/index.txt
@@ -34,6 +34,7 @@ Psycopg2 2.5 or higher is required.
aggregates
fields
forms
+ functions
lookups
operations
validators
diff --git a/docs/ref/models/database-functions.txt b/docs/ref/models/database-functions.txt
index f1293ba741..336e4afc05 100644
--- a/docs/ref/models/database-functions.txt
+++ b/docs/ref/models/database-functions.txt
@@ -203,7 +203,8 @@ Now
.. versionadded:: 1.9
-Returns the database server's current date and time when the query is executed.
+Returns the database server's current date and time when the query is executed,
+typically using the SQL ``CURRENT_TIMESTAMP``.
Usage example::
@@ -211,6 +212,13 @@ Usage example::
>>> Article.objects.filter(published__lte=Now())
[<Article: How to Django>]
+.. admonition:: PostgreSQL considerations
+
+ On PostgreSQL, the SQL ``CURRENT_TIMESTAMP`` returns the time that the
+ current transaction started. Therefore for cross-database compatibility,
+ ``Now()`` uses ``STATEMENT_TIMESTAMP`` instead. If you need the transaction
+ timestamp, use :class:`django.contrib.postgres.functions.TransactionNow`.
+
Substr
------
diff --git a/docs/releases/1.9.txt b/docs/releases/1.9.txt
index e858838b4e..e1f7439b88 100644
--- a/docs/releases/1.9.txt
+++ b/docs/releases/1.9.txt
@@ -141,13 +141,18 @@ Minor features
* Added support for the :lookup:`rangefield.contained_by` lookup for some built
in fields which correspond to the range fields.
+
* Added :class:`~django.contrib.postgres.fields.JSONField`.
+
* Added :doc:`/ref/contrib/postgres/aggregates`.
* Fixed serialization of
:class:`~django.contrib.postgres.fields.DateRangeField` and
:class:`~django.contrib.postgres.fields.DateTimeRangeField`.
+* Added the :class:`~django.contrib.postgres.functions.TransactionNow` database
+ function.
+
:mod:`django.contrib.redirects`
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/tests/postgres_tests/migrations/0002_create_test_models.py b/tests/postgres_tests/migrations/0002_create_test_models.py
index f1d06cc2d9..c8fe842397 100644
--- a/tests/postgres_tests/migrations/0002_create_test_models.py
+++ b/tests/postgres_tests/migrations/0002_create_test_models.py
@@ -130,6 +130,13 @@ class Migration(migrations.Migration):
('related_field', models.ForeignKey('postgres_tests.AggregateTestModel', null=True)),
]
),
+ migrations.CreateModel(
+ name='NowTestModel',
+ fields=[
+ ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
+ ('when', models.DateTimeField(null=True, default=None)),
+ ]
+ ),
]
pg_92_operations = [
diff --git a/tests/postgres_tests/models.py b/tests/postgres_tests/models.py
index bef77b7b21..4233092c79 100644
--- a/tests/postgres_tests/models.py
+++ b/tests/postgres_tests/models.py
@@ -109,3 +109,7 @@ class StatTestModel(models.Model):
int1 = models.IntegerField()
int2 = models.IntegerField()
related_field = models.ForeignKey(AggregateTestModel, null=True)
+
+
+class NowTestModel(models.Model):
+ when = models.DateTimeField(null=True, default=None)
diff --git a/tests/postgres_tests/test_functions.py b/tests/postgres_tests/test_functions.py
new file mode 100644
index 0000000000..620b561325
--- /dev/null
+++ b/tests/postgres_tests/test_functions.py
@@ -0,0 +1,28 @@
+from datetime import datetime
+from time import sleep
+
+from django.contrib.postgres.functions import TransactionNow
+
+from . import PostgreSQLTestCase
+from .models import NowTestModel
+
+
+class TestTransactionNow(PostgreSQLTestCase):
+
+ def test_transaction_now(self):
+ """
+ The test case puts everything under a transaction, so two models
+ updated with a short gap should have the same time.
+ """
+ m1 = NowTestModel.objects.create()
+ m2 = NowTestModel.objects.create()
+
+ NowTestModel.objects.filter(id=m1.id).update(when=TransactionNow())
+ sleep(0.1)
+ NowTestModel.objects.filter(id=m2.id).update(when=TransactionNow())
+
+ m1.refresh_from_db()
+ m2.refresh_from_db()
+
+ self.assertIsInstance(m1.when, datetime)
+ self.assertEqual(m1.when, m2.when)