浏览代码

Fixed #27996 -- Added RandomUUID function and CryptoExtension to contrib.postgres.

Paolo Melchiorre 8 年之前
父节点
当前提交
fcb5dbfec0

+ 10 - 1
django/contrib/postgres/functions.py

@@ -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()
+        super().__init__(output_field=output_field, **extra)
 
 
 class TransactionNow(Func):

+ 6 - 0
django/contrib/postgres/operations.py

@@ -35,6 +35,12 @@ class CITextExtension(CreateExtension):
         self.name = 'citext'
 
 
+class CryptoExtension(CreateExtension):
+
+    def __init__(self):
+        self.name = 'pgcrypto'
+
+
 class HStoreExtension(CreateExtension):
 
     def __init__(self):

+ 20 - 0
docs/ref/contrib/postgres/functions.txt

@@ -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``
 ==================
 

+ 9 - 0
docs/ref/contrib/postgres/operations.txt

@@ -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``
 ===================
 

+ 6 - 0
docs/releases/2.0.txt

@@ -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`
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

+ 4 - 2
tests/postgres_tests/migrations/0001_setup_extensions.py

@@ -4,13 +4,14 @@ from django.db import migrations
 
 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()
@@ -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(),

+ 7 - 0
tests/postgres_tests/migrations/0002_create_test_models.py

@@ -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=[

+ 4 - 0
tests/postgres_tests/models.py

@@ -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)

+ 15 - 2
tests/postgres_tests/test_functions.py

@@ -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):
@@ -26,3 +27,15 @@ class TestTransactionNow(PostgreSQLTestCase):
 
         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)