PhoebeB
PhoebeB

Reputation: 8570

Using UUIDField in Django keeps creating new migrations

I have a simple model::

class HasUUID(models.Model):
      name = models.CharField(max_length=10)
      batchid = models.UUIDField(default=uuid.uuid4(), unique=True)

running makemigrations gives me the migration::

   operations = [
    migrations.CreateModel(
        name='HasUUID',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ('name', models.CharField(max_length=10)),
            ('batchid', models.UUIDField(default=uuid.UUID('79a2d9fe-e1d0-4d4b-884f-fad0bfb14f0f'), unique=True)),
        ],
    ),

]

Running migrate gives me the new table no problem. But I can run makemigrations again and I get::

    operations = [
    migrations.AlterField(
        model_name='hasuuid',
        name='batchid',
        field=models.UUIDField(default=uuid.UUID('3b96231c-5848-430b-aa90-b6e41b11fd0a'), unique=True),
    ),

]

and while I have lived with this for a while, manually removing the unnecessary code, I need to resolve it.

so I think, make the default a separate function in the migrations as shown in various examples::

def create_uuid(apps, schema_editor):
      m = apps.get_model('web', 'HasUUID')
      for inst in m.objects.all():
          inst.batchid = uuid.uuid4()
          inst.save()

...

  migrations.CreateModel(
       name='HasUUID',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ('name', models.CharField(max_length=10)),
            ('batchid', models.UUIDField(blank=True, null=True)),

        ],


    ),
    migrations.RunPython(create_uuid),
    migrations.AlterField(
        model_name='hasuuid2',
        name='batchid',
        field=models.UUIDField(default=uuid.uuid4, unique=True)
    ),

same problem. So I tried making the default a separate function in the model::

  def create_uuid():
      return uuid.uuid4()


  class HasUUID2(models.Model):
      name = models.CharField(max_length=10)
      batchid = models.UUIDField(default=create_uuid(), unique=True)

and this gets me this migration::

  migrations.CreateModel(
        name='HasUUID3',
        fields=[
            ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
            ('name', models.CharField(max_length=10)),
            ('batchid', models.UUIDField(default=uuid.UUID('335c3651-b04e-4ed8-a91d-f2da3f53dd8f'), unique=True)),
        ],
    ),

and again, keeps generating new migrations. I've also tried without the unique = True.

I'm out of ideas. There must be some settings or code elsewhere as I have used UUID fields before and I cannot find a similar problem on stackoverflow. Any suggestions?

Upvotes: 3

Views: 973

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 476740

You should pass the callable as default, not the result of a call, like:

class HasUUID(models.Model):
      name = models.CharField(max_length=10)
      batchid = models.UUIDField(default=uuid.uuid4, unique=True)

Notice that there are no paratheses here to make the call, we thus pass a reference to the uuid4 function itself.

The default= value is not a specific UUID (that is determined when you start the server), it should be a value that is determined when you create a new object (without specifying the batchid yourself).

By passing a callable, Django will understand that the default is the result of a call to the callable, and it will encode that in the migration. By calling the function, you retrieve the result of the call, and each time you run makemigrations, Django will think that you changed your mind on what should be the default value (it will first think you want to use '3b96231c-5848-430b-aa90-b6e41b11fd0a' as default, and later that you want to use '335c3651-b04e-4ed8-a91d-f2da3f53dd8f'). By passing a callable, the value you pass as default remains the same.

Upvotes: 9

Related Questions