Hi,

did you got any response or did you managed to solve your problem?
I've just started to search for a UUIDField, and it would be great to
have proper uuid columns for postgres.

If so do you plan to commit it to django_extensions?

Thanks, Viktor

On 2008 dec. 16, 22:53, gordyt <gor...@gmail.com> wrote:
> Howdy Folks!
>
> I wanted to be able to have UUID primary keys for certain models.
> I found the implementation done by the django_extensions project
> (http://code.google.com/p/django-command-extensions/) and it works
> fine
> as is.
>
> But I wanted to be able to have a UUIDField that stored its values in
> an
> actual 'uuid' column if we were using PostgreSQL.  For all other
> databases
> it would use the same char(36) field as in the original
> implementation.
> This was for performance reasons, by the way.
>
> So listed below was my attempt at creating a UUIDField (based on the
> django_extensions one) that would do that.  It works great in every
> situation EXCEPT if I am working with a model that inherits from
> another
> model.
>
> Before I go any further, let me say that I've posted all of this
> on a web site so that it can be seen with nicer formatting than what
> is
> possible in a newsgroup post.  The URL for that page is:
>
> http://www.gordontillman.info/computers/41-django/94-django-uuidfield...
>
> -- or --
>
> http://tinyurl.com/6rnsya
>
> I've included the complete UUIDField definition, some sample models,
> and a small test case.
>
> Here is an interesting part.  The to_python() method of the UUIDField
> is supposed to return an instance of uuid.UUID.  This is my original
> implementation of that method:
>
>     def to_python(self, value):
>         if not value:
>             return None
>         if isinstance(value, uuid.UUID):
>             return value
>         # attempt to parse a UUID
>         return uuid.UUID(smart_unicode(value))
>
> This original implementation words great unless I'm working with a
> model that inherits from a base model that uses the UUIDField as its
> primary key.  If I change the implementation to this then the test
> case
> passes, both for the base model and the inherited model.  But of
> course
> now it is returning a string, not a uuid.UUID value.
>
>     def to_python(self, value):
>         if not value:
>             return None
>         if isinstance(value, uuid.UUID):
>             return smart_unicode(value)
>         else:
>             return value
>
> I was wondering if anyone could suggest an improvement to my
> UUIDField implementation so that (1) to_python() returns a proper
> uuid.UUID instance and (2) I can still use 'uuid' columns in
> databases that support it.
>
> It's possible there is a bug in the part of the Django code that
> deals with inherited models, but I'm sure it's way more likely
> that there is a bug in my UUIDField!
>
> ---------- begin UUIDField ----------
> import uuid
>
> from django.forms.util import ValidationError
> from django import forms
> from django.db import models
> from django.utils.encoding import smart_unicode
> from django.utils.translation import ugettext_lazy
>
> class UUIDVersionError(Exception):
>     pass
>
> class UUIDField(models.Field):
>     """Encode and stores a Python uuid.UUID in a manner that is
> appropriate
>     for the given datatabase that we are using.
>
>     For sqlite3 or MySQL we save it as a 36-character string value
>     For PostgreSQL we save it as a uuid field
>
>     This class supports type 1, 2, 4, and 5 UUID's.
>     """
>     __metaclass__ = models.SubfieldBase
>
>     _CREATE_COLUMN_TYPES = {
>         'postgresql_psycopg2': 'uuid',
>         'postgresql': 'uuid'
>     }
>
>     def __init__(self, verbose_name=None, name=None, auto=True,
> version=1,
>         node=None, clock_seq=None, namespace=None, **kwargs):
>         """Contruct a UUIDField.
>
>         @param verbose_name: Optional verbose name to use in place of
> what
>             Django would assign.
>         @param name: Override Django's name assignment
>         @param auto: If True, create a UUID value if one is not
> specified.
>         @param version: By default we create a version 1 UUID.
>         @param node: Used for version 1 UUID's.  If not supplied, then
> the
>             uuid.getnode() function is called to obtain it.  This can
> be slow.
>         @param clock_seq: Used for version 1 UUID's.  If not supplied
> a random
>             14-bit sequence number is chosen
>         @param namespace: Required for version 3 and version 5 UUID's.
>         @param name: Required for version4 and version 5 UUID's.
>
>         See Also:
>           - Python Library Reference, section 18.16 for more
> information.
>           - RFC 4122, "A Universally Unique IDentifier (UUID) URN
> Namespace"
>
>         If you want to use one of these as a primary key for a Django
>         model, do this::
>             id = UUIDField(primary_key=True)
>         This will currently I{not} work with Jython because PostgreSQL
> support
>         in Jython is not working for uuid column types.
>         """
>         self.max_length = 36
>         kwargs['max_length'] = self.max_length
>         if auto:
>             kwargs['blank'] = True
>             kwargs.setdefault('editable', False)
>
>         self.auto = auto
>         self.version = version
>         if version==1:
>             self.node, self.clock_seq = node, clock_seq
>         elif version==3 or version==5:
>             self.namespace, self.name = namespace, name
>
>         super(UUIDField, self).__init__(verbose_name=verbose_name,
>             name=name, **kwargs)
>
>     def create_uuid(self):
>         if not self.version or self.version==4:
>             return uuid.uuid4()
>         elif self.version==1:
>             return uuid.uuid1(self.node, self.clock_seq)
>         elif self.version==2:
>             raise UUIDVersionError("UUID version 2 is not supported.")
>         elif self.version==3:
>             return uuid.uuid3(self.namespace, self.name)
>         elif self.version==5:
>             return uuid.uuid5(self.namespace, self.name)
>         else:
>             raise UUIDVersionError("UUID version %s is not valid." %
> self.version)
>
>     def db_type(self):
>         from django.conf import settings
>         return UUIDField._CREATE_COLUMN_TYPES.get
> (settings.DATABASE_ENGINE,
>             "char(%s)" % self.max_length)
>
>     def to_python(self, value):
>         """Return a uuid.UUID instance from the value returned by the
>         database."""
>         #
>         # This is the proper way... But this doesn't work correctly
> when
>         # working with an inherited model
>         #
>         if not value:
>             return None
>         if isinstance(value, uuid.UUID):
>             return value
>         # attempt to parse a UUID
>         return uuid.UUID(smart_unicode(value))
>
>         #
>         # If I do the following (returning a String instead of a UUID
>         # instance), everything works.
>         #
>
>         #if not value:
>         #    return None
>         #if isinstance(value, uuid.UUID):
>         #    return smart_unicode(value)
>         #else:
>         #    return value
>
>     def pre_save(self, model_instance, add):
>         if self.auto and add:
>             value = self.create_uuid()
>             setattr(model_instance, self.attname, value)
>         else:
>             value = super(UUIDField, self).pre_save(model_instance,
> add)
>             if self.auto and not value:
>                 value = self.create_uuid()
>                 setattr(model_instance, self.attname, value)
>         return value
>
>     def get_db_prep_value(self, value):
>         """Casts uuid.UUID values into the format expected by the back
> end
>         for use in queries"""
>         if isinstance(value, uuid.UUID):
>             return smart_unicode(value)
>         return value
>
>     def value_to_string(self, obj):
>         val = self._get_val_from_obj(obj)
>         if val is None:
>             data = ''
>         else:
>             data = smart_unicode(val)
>         return data
>
>     def formfield(self, **kwargs):
>         defaults = {
>             'form_class': forms.CharField,
>             'max_length': self.max_length
>             }
>         defaults.update(kwargs)
>         return super(UUIDField, self).formfield(**defaults)
> ---------- end UUIDField ----------
>
> ---------- begin sample models ----------
>
> from django.db import models
> from fields import UUIDField
>
> class Customer(models.Model):
>     MAX_NAME_LEN = 200
>     id = UUIDField(primary_key=True)
>     name = models.CharField(max_length=MAX_NAME_LEN)
>
> class User(models.Model):
>     MAX_FIRST_NAME = 32
>     MAX_LAST_NAME = 32
>     MAX_USERNAME = 32
>
>     id = UUIDField(primary_key=True)
>     customer = models.ForeignKey(Customer)
>     first_name = models.CharField(max_length=MAX_FIRST_NAME)
>     last_name = models.CharField(max_length=MAX_LAST_NAME)
>     username = models.CharField(max_length=MAX_USERNAME)
>     password = models.CharField(max_length=128, blank=True)
>
> class Teacher(User):
>     email = models.EmailField()
>     admin = models.BooleanField(default=False)
> ---------- end sample models ----------
>
> ---------- begin sample test ----------
> import logging
> import random
> import unittest
> import time
> import sys
> from models import *
>
> class InheritanceTestCase(unittest.TestCase):
>     """
>     python manage.py test hb7t.InheritanceTestCase
>     """
>
>     def runTest(self):
>         c = Customer(name='cust1')
>         c.save()
>         u = User(customer=c, first_name='f', last_name='l',
> username='fl',
>             password='p')
>         u.save()
>         self.assertEqual(1, Customer.objects.count())
>         self.assertEqual(1, User.objects.count())
>         self.assertEqual(0, Teacher.objects.count())
>
>         t = Teacher(customer=c, first_name='g', last_name='t',
> username='gt',
>             password='p', email=...@test.com')
>         # Everything passes up to this point, whether or not to_python
> () returns
>         # a uuid.UUID value OR a string value
>         # But t.save() fails if to_python() returns a uuid.UUID value
>         t.save()
>         self.assertEqual(1, Customer.objects.count())
>         self.assertEqual(2, User.objects.count())
>         self.assertEqual(1, Teacher.objects.count())
>
>         c.delete()
>         self.assertEqual(0, Customer.objects.count())
>         self.assertEqual(0, User.objects.count())
>         self.assertEqual(0, Teacher.objects.count())
>
> ---------- end sample test ----------
>
> ERROR: runTest (kindle.hb7t.tests.InheritanceTestCase)
> ----------------------------------------------------------------------
> Traceback (most recent call last):
>   File "/home/gordy/projects/kindle/../kindle/hb7t/tests.py", line
> 118, in runTest
>     t.save()
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> base.py", line 328, in save
>     self.save_base(force_insert=force_insert,
> force_update=force_update)
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> base.py", line 375, in save_base
>     manager.filter(pk=pk_val).extra(select={'a': 1}).values
> ('a').order_by())):
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> query.py", line 191, in __nonzero__
>     iter(self).next()
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> query.py", line 185, in _result_iter
>     self._fill_cache()
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> query.py", line 618, in _fill_cache
>     self._result_cache.append(self._iter.next())
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/
> query.py", line 659, in iterator
>     for row in self.query.results_iter():
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/sql/
> query.py", line 203, in results_iter
>     for rows in self.execute_sql(MULTI):
>   File "/usr/local/lib/python2.5/site-packages/django/db/models/sql/
> query.py", line 1756, in execute_sql
>     cursor.execute(sql, params)
> ProgrammingError: can't adapt
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to 
django-users+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to