mirror of
https://github.com/Hopiu/django-model-utils.git
synced 2026-03-16 20:00:23 +00:00
Merge branch 'feature/uuid_model'
This commit is contained in:
commit
2f29dce4de
9 changed files with 200 additions and 8 deletions
|
|
@ -50,4 +50,4 @@
|
|||
| Jack Cushman <jcushman@law.harvard.edu>
|
||||
| Zach Cheung <kuroro.zhang@gmail.com>
|
||||
| Daniel Andrlik <daniel@andrlik.org>
|
||||
|
||||
| marfyl <github.com/marfyl>
|
||||
|
|
@ -154,3 +154,29 @@ If no marker is found in the content, the first two paragraphs (where
|
|||
paragraphs are blocks of text separated by a blank line) are taken to
|
||||
be the excerpt. This number can be customized by setting the
|
||||
``SPLIT_DEFAULT_PARAGRAPHS`` setting.
|
||||
|
||||
|
||||
UUIDField
|
||||
----------
|
||||
|
||||
A ``UUIDField``subclass that provides an UUID field. You can
|
||||
add this field to any model definition.
|
||||
|
||||
With the param ``primary_key`` you can set if this field is the
|
||||
primary key for the model, default is True.
|
||||
|
||||
Param ``version`` is an integer that set default UUID version.
|
||||
Versions 1,3,4 and 5 are supported, default is 4.
|
||||
|
||||
If ``editable`` is set to false the field will not be displayed in the admin
|
||||
or any other ModelForm, default is False.
|
||||
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from django.db import models
|
||||
from model_utils.fields import UUIDField
|
||||
|
||||
class MyAppModel(models.Model):
|
||||
uuid = UUIDField(primary_key=True, version=4, editable=False)
|
||||
|
||||
|
|
|
|||
|
|
@ -55,3 +55,24 @@ SoftDeletableModel
|
|||
This abstract base class just provides field ``is_removed`` which is
|
||||
set to True instead of removing the instance. Entities returned in
|
||||
default manager are limited to not-deleted instances.
|
||||
|
||||
|
||||
UUIDModel
|
||||
------------------
|
||||
|
||||
This abstract base class provides ``id`` field on any model that inherits from it
|
||||
which will be the primary key.
|
||||
|
||||
If you dont want to set ``id`` as primary key or change the field name, you can be override it
|
||||
with our [UUIDField](https://github.com/jazzband/django-model-utils/blob/master/docs/fields.rst#uuidfield).
|
||||
|
||||
Also you can override the default uuid version. Versions 1,3,4 and 5 are now supported.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
from model_utils.models import UUIDModel
|
||||
from model_utils import Choices
|
||||
|
||||
class MyAppModel(UUIDModel):
|
||||
pass
|
||||
|
||||
|
|
|
|||
2
model_utils/exceptions.py
Normal file
2
model_utils/exceptions.py
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
class UUIDVersionException(Exception):
|
||||
pass
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import django
|
||||
import uuid
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils.encoding import python_2_unicode_compatible
|
||||
from django.utils.timezone import now
|
||||
|
||||
from model_utils.exceptions import UUIDVersionException
|
||||
|
||||
DEFAULT_CHOICES_NAME = 'STATUS'
|
||||
|
||||
|
||||
|
|
@ -17,6 +20,7 @@ class AutoCreatedField(models.DateTimeField):
|
|||
By default, sets editable=False, default=datetime.now.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('editable', False)
|
||||
kwargs.setdefault('default', now)
|
||||
|
|
@ -30,6 +34,7 @@ class AutoLastModifiedField(AutoCreatedField):
|
|||
By default, sets editable=False and default=datetime.now.
|
||||
|
||||
"""
|
||||
|
||||
def pre_save(self, model_instance, add):
|
||||
value = now()
|
||||
if not model_instance.pk:
|
||||
|
|
@ -53,6 +58,7 @@ class StatusField(models.CharField):
|
|||
Also features a ``no_check_for_status`` argument to make sure
|
||||
South can handle this field when it freezes a model.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('max_length', 100)
|
||||
self.check_for_status = not kwargs.pop('no_check_for_status', False)
|
||||
|
|
@ -93,6 +99,7 @@ class MonitorField(models.DateTimeField):
|
|||
changes.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.setdefault('default', now)
|
||||
monitor = kwargs.pop('monitor', None)
|
||||
|
|
@ -144,6 +151,7 @@ SPLIT_MARKER = getattr(settings, 'SPLIT_MARKER', '<!-- split -->')
|
|||
# the number of paragraphs after which to split if no marker
|
||||
SPLIT_DEFAULT_PARAGRAPHS = getattr(settings, 'SPLIT_DEFAULT_PARAGRAPHS', 2)
|
||||
|
||||
|
||||
_excerpt_field_name = lambda name: '_%s_excerpt' % name
|
||||
|
||||
|
||||
|
|
@ -252,3 +260,45 @@ class SplitField(models.TextField):
|
|||
name, path, args, kwargs = super(SplitField, self).deconstruct()
|
||||
kwargs['no_excerpt_field'] = True
|
||||
return name, path, args, kwargs
|
||||
|
||||
|
||||
class UUIDField(models.UUIDField):
|
||||
"""
|
||||
A field for storing universally unique identifiers. Uses Python’s UUID class.
|
||||
"""
|
||||
|
||||
def __init__(self, primary_key=True, version=4, editable=False, *args, **kwargs):
|
||||
"""
|
||||
Parameters
|
||||
----------
|
||||
primary_key : bool
|
||||
If True, this field is the primary key for the model.
|
||||
version : int
|
||||
An integer that set default UUID version.
|
||||
editable : bool
|
||||
If False, the field will not be displayed in the admin or any other ModelForm,
|
||||
default is false.
|
||||
|
||||
Raises
|
||||
------
|
||||
UUIDVersionException
|
||||
UUID version 2 is not supported.
|
||||
"""
|
||||
kwargs.setdefault('primary_key', primary_key)
|
||||
kwargs.setdefault('editable', editable)
|
||||
|
||||
if version == 4:
|
||||
default = uuid.uuid4
|
||||
elif version == 1:
|
||||
default = uuid.uuid1
|
||||
elif version == 2:
|
||||
raise UUIDVersionException("UUID version 2 is not supported.")
|
||||
elif version == 3:
|
||||
default = uuid.uuid3
|
||||
elif version == 5:
|
||||
default = uuid.uuid5
|
||||
else:
|
||||
raise UUIDVersionException("UUID version %s is not valid." % version)
|
||||
|
||||
kwargs.setdefault('default', default)
|
||||
super(UUIDField, self).__init__(*args, **kwargs)
|
||||
|
|
|
|||
|
|
@ -4,16 +4,25 @@ import django
|
|||
from django.core.exceptions import ImproperlyConfigured
|
||||
from django.db import models
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from model_utils.fields import (
|
||||
AutoCreatedField,
|
||||
AutoLastModifiedField,
|
||||
StatusField,
|
||||
MonitorField,
|
||||
UUIDField,
|
||||
)
|
||||
from model_utils.managers import (
|
||||
QueryManager,
|
||||
SoftDeletableManager,
|
||||
)
|
||||
|
||||
if django.VERSION >= (1, 9, 0):
|
||||
from django.db.models.functions import Now
|
||||
now = Now()
|
||||
else:
|
||||
from django.utils.timezone import now
|
||||
|
||||
from model_utils.managers import QueryManager, SoftDeletableManager
|
||||
from model_utils.fields import AutoCreatedField, AutoLastModifiedField, \
|
||||
StatusField, MonitorField
|
||||
|
||||
|
||||
class TimeStampedModel(models.Model):
|
||||
"""
|
||||
|
|
@ -135,3 +144,18 @@ class SoftDeletableModel(models.Model):
|
|||
self.save(using=using)
|
||||
else:
|
||||
return super(SoftDeletableModel, self).delete(using=using, *args, **kwargs)
|
||||
|
||||
|
||||
class UUIDModel(models.Model):
|
||||
"""
|
||||
This abstract base class provides id field on any model that inherits from it
|
||||
which will be the primary key.
|
||||
"""
|
||||
id = UUIDField(
|
||||
primary_key=True,
|
||||
version=4,
|
||||
editable=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
|
|
|||
|
|
@ -8,7 +8,12 @@ from django.utils.encoding import python_2_unicode_compatible
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from model_utils import Choices
|
||||
from model_utils.fields import SplitField, MonitorField, StatusField
|
||||
from model_utils.fields import (
|
||||
SplitField,
|
||||
MonitorField,
|
||||
StatusField,
|
||||
UUIDField,
|
||||
)
|
||||
from model_utils.managers import (
|
||||
QueryManager,
|
||||
InheritanceManager,
|
||||
|
|
@ -19,6 +24,7 @@ from model_utils.models import (
|
|||
StatusModel,
|
||||
TimeFramedModel,
|
||||
TimeStampedModel,
|
||||
UUIDModel,
|
||||
)
|
||||
from tests.fields import MutableField
|
||||
from tests.managers import CustomSoftDeleteManager
|
||||
|
|
@ -159,8 +165,8 @@ class Post(models.Model):
|
|||
|
||||
objects = models.Manager()
|
||||
public = QueryManager(published=True)
|
||||
public_confirmed = QueryManager(models.Q(published=True) &
|
||||
models.Q(confirmed=True))
|
||||
public_confirmed = QueryManager(models.Q(published=True)
|
||||
& models.Q(confirmed=True))
|
||||
public_reversed = QueryManager(published=True).order_by("-order")
|
||||
|
||||
class Meta:
|
||||
|
|
@ -340,6 +346,7 @@ class StringyDescriptor(object):
|
|||
"""
|
||||
Descriptor that returns a string version of the underlying integer value.
|
||||
"""
|
||||
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
|
|
@ -393,3 +400,11 @@ class JoinItemForeignKey(models.Model):
|
|||
on_delete=models.CASCADE
|
||||
)
|
||||
objects = JoinManager()
|
||||
|
||||
|
||||
class CustomUUIDModel(UUIDModel):
|
||||
pass
|
||||
|
||||
|
||||
class CustomNotPrimaryUUIDModel(models.Model):
|
||||
uuid = UUIDField(primary_key=False)
|
||||
|
|
|
|||
34
tests/test_fields/test_uuid_field.py
Normal file
34
tests/test_fields/test_uuid_field.py
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import uuid
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from model_utils.fields import UUIDField
|
||||
from model_utils.exceptions import UUIDVersionException
|
||||
|
||||
|
||||
class UUIDFieldTests(TestCase):
|
||||
|
||||
def test_uuid_version_default(self):
|
||||
instance = UUIDField()
|
||||
self.assertEqual(instance.default, uuid.uuid4)
|
||||
|
||||
def test_uuid_version_1(self):
|
||||
instance = UUIDField(version=1)
|
||||
self.assertEqual(instance.default, uuid.uuid1)
|
||||
|
||||
def test_uuid_version_2_error(self):
|
||||
self.assertRaises(UUIDVersionException, UUIDField, 'version', 2)
|
||||
|
||||
def test_uuid_version_3(self):
|
||||
instance = UUIDField(version=3)
|
||||
self.assertEqual(instance.default, uuid.uuid3)
|
||||
|
||||
def test_uuid_version_4(self):
|
||||
instance = UUIDField(version=4)
|
||||
self.assertEqual(instance.default, uuid.uuid4)
|
||||
|
||||
def test_uuid_version_5(self):
|
||||
instance = UUIDField(version=5)
|
||||
self.assertEqual(instance.default, uuid.uuid5)
|
||||
20
tests/test_models/test_uuid_model.py
Normal file
20
tests/test_models/test_uuid_model.py
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
from tests.models import CustomUUIDModel, CustomNotPrimaryUUIDModel
|
||||
|
||||
|
||||
class UUIDFieldTests(TestCase):
|
||||
|
||||
def test_uuid_model_with_uuid_field_as_primary_key(self):
|
||||
instance = CustomUUIDModel()
|
||||
instance.save()
|
||||
self.assertEqual(instance.id.__class__.__name__, 'UUID')
|
||||
self.assertEqual(instance.id, instance.pk)
|
||||
|
||||
def test_uuid_model_with_uuid_field_as_not_primary_key(self):
|
||||
instance = CustomNotPrimaryUUIDModel()
|
||||
instance.save()
|
||||
self.assertEqual(instance.uuid.__class__.__name__, 'UUID')
|
||||
self.assertNotEqual(instance.uuid, instance.pk)
|
||||
Loading…
Reference in a new issue