lukesneeringer / django-pgfields

Django field subclasses for some PostgreSQL types.
Other
67 stars 16 forks source link

Django 1.7 model UUIDField - problem with admin inlines / migrations #15

Open orporat opened 9 years ago

orporat commented 9 years ago

Hi,

I'm testing django-pgfields 1.5.0a1 with Django 1.7rc2 and I have a problem with admin inlines. These are my models:

class Elf(models.Model):
    id = models.UUIDField(auto_add=True, primary_key=True)
    name = models.CharField(max_length=50)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

class RelatedToElf(models.Model):
    id = models.UUIDField(auto_add=True, primary_key=True)
    name = models.CharField(max_length=50)
    elf = models.ForeignKey(Elf, related_name="elfs")

And here's my admin configuration:

class RelatedElfInline(admin.TabularInline):
    model = models.RelatedToElf

class ElfAdmin(admin.ModelAdmin):
    inlines = [RelatedElfInline,]

admin.site.register(models.Elf, ElfAdmin)

As you might notice, in the models definition I dont have default=uuid.uuid4 on the id fields but I do have auto_add=True. This is because setting a default on these fields seems to be causing inline rows in the admin to be pre-filled with the uuid4 values when the form is rendered. When the form is saved, Django marks these inlines as 'changed' effectively trying to create them as new objects thus triggering form validation for all of these inlines requireing values for all form fields that doesn't have null=True, blank=True (e.g. "name" in this case). This happens even if the id field is hidden.

With the models' definition mentioned above inlines work well, but when I try to apply a migration, this is the result (I've applied it once with default=uuid.uuid4 and then removed it):

Running migrations:
  Applying myapp.0002_auto_20140730_0000...Traceback (most recent call last):
  File "manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 385, in execute_from_command_line
    utility.execute()
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/core/management/__init__.py", line 377, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 288, in run_from_argv
    self.execute(*args, **options.__dict__)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/core/management/base.py", line 337, in execute
    output = self.handle(*args, **options)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/core/management/commands/migrate.py", line 160, in handle
    executor.migrate(targets, plan, fake=options.get("fake", False))
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 63, in migrate
    self.apply_migration(migration, fake=fake)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/migrations/executor.py", line 97, in apply_migration
    migration.apply(project_state, schema_editor)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/migrations/migration.py", line 107, in apply
    operation.database_forwards(self.app_label, schema_editor, project_state, new_state)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/migrations/operations/fields.py", line 131, in database_forwards
    schema_editor.alter_field(from_model, from_field, to_field)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/backends/schema.py", line 509, in alter_field
    self._alter_field(model, old_field, new_field, old_type, new_type, old_db_params, new_db_params, strict)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/backends/schema.py", line 610, in _alter_field
    new_default = self.effective_default(new_field)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/backends/schema.py", line 183, in effective_default
    default = field.get_db_prep_save(default, self.connection)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 627, in get_db_prep_save
    prepared=False)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django_pg/models/fields/uuid.py", line 83, in get_db_prep_value
    prepared=prepared)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django/db/models/fields/__init__.py", line 619, in get_db_prep_value
    value = self.get_prep_value(value)
  File "/home/user/ve/myproject/local/lib/python2.7/site-packages/django_pg/models/fields/uuid.py", line 64, in get_prep_value
    raise ValueError('Explicit UUID required unless either `null` is '
ValueError: Explicit UUID required unless either `null` is True or `auto_add` is given.

Help, please :)

Thanks very much!

lukesneeringer commented 9 years ago

Hi @cheerzorpo, I'll look into this. Thanks!

budlight commented 9 years ago

I think you can get around the migration error with

import uuid
class MyModel(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4)

A better error message is probably necessary, also the default django fields let you define date time callables for new time fields, something like that isn't necessary but could be nice.

orporat commented 9 years ago

Hi @budlight,

This would require me to add default back every time I create a migration and take it off after the migration is applied, in order that I wouldn't have problems with admin inlines.

Plus, the exception says:

ValueError: Explicit UUID required unless either `null` is True or `auto_add` is given.

I did set auto_add=True in my example, so isn't this a bug anyway? :P

Thanks

budlight commented 9 years ago

not the same thing?

orporat commented 9 years ago

auto_add=True in contrast to default doesn't result in problems with admin inlines, but the exception in the migration process is being thrown even though the field has auto_add=True (which by the exception message doesn't supposed to happen)

nanuxbe commented 9 years ago

I am having the same problem on Django 1.7 during migration. Adding a default value does fix the problem.

It looks like during the migration the migration process the auto_add parameter gets dropped (maybe has to do with deconstruct, I am not sure) but here is a dump of the value of auto_add and kwargs during a migration if it helps: True <-> {'default': <function uuid4 at 0x28c0cf8>, 'primary_key': True} False <-> {u'default': <function uuid4 at 0x28c0cf8>, u'serialize': False, u'unique': True, u'editable': False, u'primary_key': True} False <-> {u'default': <function uuid4 at 0x28c0cf8>, u'serialize': False, u'unique': True, u'editable': False, u'primary_key': True} False <-> {u'default': <function uuid4 at 0x28c0cf8>, u'serialize': False, u'unique': True, u'editable': False, u'primary_key': True} False <-> {u'default': <function uuid4 at 0x28c0cf8>, u'serialize': False, u'unique': True, u'editable': False, u'primary_key': True}