Skip to content
Merged
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
11 changes: 10 additions & 1 deletion django/contrib/postgres/functions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@
from django.db.models import DateTimeField, Func
from django.db.models import DateTimeField, Func, UUIDField


class RandomUUID(Func):
template = 'GEN_RANDOM_UUID()'

def __init__(self, output_field=None, **extra):
if output_field is None:
output_field = UUIDField()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could use _output_field = UUIDField() at the class level instead of this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's slightly too much magic than I'd like, I think. Thoughts on that @jarshwah?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used the same convention that I found in the same file for TransactionNow function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then it's settled, I think. Thanks :)

super().__init__(output_field=output_field, **extra)


class TransactionNow(Func):
Expand Down
6 changes: 6 additions & 0 deletions django/contrib/postgres/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ def __init__(self):
self.name = 'citext'


class CryptoExtension(CreateExtension):

def __init__(self):
self.name = 'pgcrypto'


class HStoreExtension(CreateExtension):

def __init__(self):
Expand Down
20 changes: 20 additions & 0 deletions docs/ref/contrib/postgres/functions.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,26 @@ All of these functions are available from the

.. currentmodule:: django.contrib.postgres.functions

``RandomUUID``
==============

.. class:: RandomUUID()

.. versionadded:: 2.0

Returns a version 4 UUID.

The `pgcrypto extension`_ must be installed. You can use the
:class:`~django.contrib.postgres.operations.CryptoExtension` migration
operation to install it.

.. _pgcrypto extension: https://www.postgresql.org/docs/current/static/pgcrypto.html

Usage example::

>>> from django.contrib.postgres.functions import RandomUUID
>>> Article.objects.update(uuid=RandomUUID())

``TransactionNow``
==================

Expand Down
9 changes: 9 additions & 0 deletions docs/ref/contrib/postgres/operations.txt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ run the query ``CREATE EXTENSION IF NOT EXISTS hstore;``.

Installs the ``citext`` extension.

``CryptoExtension``
===================

.. class:: CryptoExtension()

.. versionadded:: 2.0

Installs the ``pgcrypto`` extension.

``HStoreExtension``
===================

Expand Down
6 changes: 6 additions & 0 deletions docs/releases/2.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,12 @@ Minor features
:class:`~django.contrib.postgres.aggregates.ArrayAgg` determines if
concatenated values will be distinct.

* The new :class:`~django.contrib.postgres.functions.RandomUUID` database
function returns a version 4 UUID. It requires use of PostgreSQL's
``pgcrypto`` extension which can be activated using the new
:class:`~django.contrib.postgres.operations.CryptoExtension` migration
operation.

:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
6 changes: 4 additions & 2 deletions tests/postgres_tests/migrations/0001_setup_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

try:
from django.contrib.postgres.operations import (
BtreeGinExtension, CITextExtension, CreateExtension, HStoreExtension,
TrigramExtension, UnaccentExtension,
BtreeGinExtension, CITextExtension, CreateExtension, CryptoExtension,
HStoreExtension, TrigramExtension, UnaccentExtension,
)
except ImportError:
BtreeGinExtension = mock.Mock()
CITextExtension = mock.Mock()
CreateExtension = mock.Mock()
CryptoExtension = mock.Mock()
HStoreExtension = mock.Mock()
TrigramExtension = mock.Mock()
UnaccentExtension = mock.Mock()
Expand All @@ -24,6 +25,7 @@ class Migration(migrations.Migration):
# Ensure CreateExtension quotes extension names by creating one with a
# dash in its name.
CreateExtension('uuid-ossp'),
CryptoExtension(),
HStoreExtension(),
TrigramExtension(),
UnaccentExtension(),
Expand Down
7 changes: 7 additions & 0 deletions tests/postgres_tests/migrations/0002_create_test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,13 @@ class Migration(migrations.Migration):
('when', models.DateTimeField(null=True, default=None)),
]
),
migrations.CreateModel(
name='UUIDTestModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('uuid', models.UUIDField(default=None, null=True)),
]
),
migrations.CreateModel(
name='RangesModel',
fields=[
Expand Down
4 changes: 4 additions & 0 deletions tests/postgres_tests/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,3 +171,7 @@ class StatTestModel(models.Model):

class NowTestModel(models.Model):
when = models.DateTimeField(null=True, default=None)


class UUIDTestModel(models.Model):
uuid = models.UUIDField(default=None, null=True)
17 changes: 15 additions & 2 deletions tests/postgres_tests/test_functions.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import uuid
from datetime import datetime
from time import sleep

from django.contrib.postgres.functions import TransactionNow
from django.contrib.postgres.functions import RandomUUID, TransactionNow

from . import PostgreSQLTestCase
from .models import NowTestModel
from .models import NowTestModel, UUIDTestModel


class TestTransactionNow(PostgreSQLTestCase):
Expand All @@ -26,3 +27,15 @@ def test_transaction_now(self):

self.assertIsInstance(m1.when, datetime)
self.assertEqual(m1.when, m2.when)


class TestRandomUUID(PostgreSQLTestCase):

def test_random_uuid(self):
m1 = UUIDTestModel.objects.create()
m2 = UUIDTestModel.objects.create()
UUIDTestModel.objects.update(uuid=RandomUUID())
m1.refresh_from_db()
m2.refresh_from_db()
self.assertIsInstance(m1.uuid, uuid.UUID)
self.assertNotEqual(m1.uuid, m2.uuid)