| 1 | """To recreate the error, just start an app and drop in this file.
|
|---|
| 2 | Pull up the admin interface. And add a Book. You can go ahead and add
|
|---|
| 3 | one or more Authors at the same time. Save your changes.
|
|---|
| 4 |
|
|---|
| 5 | This should work with no errors. The error occurs when you save a Book
|
|---|
| 6 | object in the Admin interface that has one or more Authors assigned to it.
|
|---|
| 7 |
|
|---|
| 8 | That is, after creating a Book with one or more Authors and saving it, go back
|
|---|
| 9 | and edit that Book again in the admin interface. Don't have to change
|
|---|
| 10 | anything, just click Save and this is the error that is generated:
|
|---|
| 11 |
|
|---|
| 12 |
|
|---|
| 13 | KeyError at /admin/uuid_test/book/6b31461e-ffa2-11dd-be8a-0019dbb65276/
|
|---|
| 14 |
|
|---|
| 15 | None
|
|---|
| 16 |
|
|---|
| 17 | Request Method: POST
|
|---|
| 18 | Request URL: http://localhost:8000/admin/uuid_test/book/6b31461e-ffa2-11dd-be8a-0019dbb65276/
|
|---|
| 19 | Exception Type: KeyError
|
|---|
| 20 | Exception Value: None
|
|---|
| 21 |
|
|---|
| 22 | Exception Location: /usr/local/lib/python2.5/site-packages/django/forms/models.py in save_existing_objects, line 411
|
|---|
| 23 | Python Executable: /usr/bin/python
|
|---|
| 24 | Python Version: 2.5.2
|
|---|
| 25 | Python Path: [...]
|
|---|
| 26 | Server time: Fri, 20 Feb 2009 17:03:31 -0600
|
|---|
| 27 | """
|
|---|
| 28 | from django.db import models
|
|---|
| 29 | from django.db.models import CharField
|
|---|
| 30 | from django.contrib import admin
|
|---|
| 31 | try:
|
|---|
| 32 | import uuid
|
|---|
| 33 | except ImportError:
|
|---|
| 34 | from django_extensions.utils import uuid
|
|---|
| 35 |
|
|---|
| 36 | # UUIDVersionError and UUIDField are taken directly from the django-extensions
|
|---|
| 37 | # project with no alterations. I bundled them in this little test case
|
|---|
| 38 | # so you wouldn't have to install django-extensions just to run the test.
|
|---|
| 39 | class UUIDVersionError(Exception):
|
|---|
| 40 | pass
|
|---|
| 41 |
|
|---|
| 42 | class UUIDField(CharField):
|
|---|
| 43 | """ UUIDField
|
|---|
| 44 |
|
|---|
| 45 | By default uses UUID version 1 (generate from host ID, sequence number and current time)
|
|---|
| 46 |
|
|---|
| 47 | The field support all uuid versions which are natively supported by the uuid python module.
|
|---|
| 48 | For more information see: http://docs.python.org/lib/module-uuid.html
|
|---|
| 49 | """
|
|---|
| 50 |
|
|---|
| 51 | def __init__(self, verbose_name=None, name=None, auto=True, version=1, node=None, clock_seq=None, namespace=None, **kwargs):
|
|---|
| 52 | kwargs['max_length'] = 36
|
|---|
| 53 | if auto:
|
|---|
| 54 | kwargs['blank'] = True
|
|---|
| 55 | kwargs.setdefault('editable', False)
|
|---|
| 56 | self.auto = auto
|
|---|
| 57 | self.version = version
|
|---|
| 58 | if version==1:
|
|---|
| 59 | self.node, self.clock_seq = node, clock_seq
|
|---|
| 60 | elif version==3 or version==5:
|
|---|
| 61 | self.namespace, self.name = namespace, name
|
|---|
| 62 | CharField.__init__(self, verbose_name, name, **kwargs)
|
|---|
| 63 |
|
|---|
| 64 | def get_internal_type(self):
|
|---|
| 65 | return CharField.__name__
|
|---|
| 66 |
|
|---|
| 67 | def create_uuid(self):
|
|---|
| 68 | if not self.version or self.version==4:
|
|---|
| 69 | return uuid.uuid4()
|
|---|
| 70 | elif self.version==1:
|
|---|
| 71 | return uuid.uuid1(self.node, self.clock_seq)
|
|---|
| 72 | elif self.version==2:
|
|---|
| 73 | raise UUIDVersionError("UUID version 2 is not supported.")
|
|---|
| 74 | elif self.version==3:
|
|---|
| 75 | return uuid.uuid3(self.namespace, self.name)
|
|---|
| 76 | elif self.version==5:
|
|---|
| 77 | return uuid.uuid5(self.namespace, self.name)
|
|---|
| 78 | else:
|
|---|
| 79 | raise UUIDVersionError("UUID version %s is not valid." % self.version)
|
|---|
| 80 |
|
|---|
| 81 | def pre_save(self, model_instance, add):
|
|---|
| 82 | if self.auto and add:
|
|---|
| 83 | value = unicode(self.create_uuid())
|
|---|
| 84 | setattr(model_instance, self.attname, value)
|
|---|
| 85 | return value
|
|---|
| 86 | else:
|
|---|
| 87 | value = super(UUIDField, self).pre_save(model_instance, add)
|
|---|
| 88 | if self.auto and not value:
|
|---|
| 89 | value = unicode(self.create_uuid())
|
|---|
| 90 | setattr(model_instance, self.attname, value)
|
|---|
| 91 | return value
|
|---|
| 92 |
|
|---|
| 93 |
|
|---|
| 94 | class Book(models.Model):
|
|---|
| 95 | id = UUIDField(primary_key=True, auto_created=False)
|
|---|
| 96 | title = models.CharField(max_length=128)
|
|---|
| 97 | def __unicode__(self):
|
|---|
| 98 | return unicode(self.title)
|
|---|
| 99 |
|
|---|
| 100 | class Author(models.Model):
|
|---|
| 101 | id = UUIDField(primary_key=True, auto_created=False)
|
|---|
| 102 | book = models.ForeignKey(Book)
|
|---|
| 103 | name = models.CharField(max_length=128)
|
|---|
| 104 | def __unicode(self):
|
|---|
| 105 | return unicode(self.name)
|
|---|
| 106 |
|
|---|
| 107 |
|
|---|
| 108 | class AuthorInlineAdmin(admin.TabularInline):
|
|---|
| 109 | model=Author
|
|---|
| 110 |
|
|---|
| 111 | class BookAdmin(admin.ModelAdmin):
|
|---|
| 112 | inlines = (AuthorInlineAdmin, )
|
|---|
| 113 | admin.site.register(Book, BookAdmin)
|
|---|