1

I'm trying to overwrite the pk of my models to use a random generator.

Here's the model field:

pk = models.AutoField(auto_created=True, primary_key=True, verbose_name='ID', default=genkey)

and the keygenerator:

def genkey():
    return random.randrange(1,142857)

So, I would like to make the autofield to execute the genkey function as much as possible until it gets a non-used key. Is there an attribute that I didn't find or do I need to code it in the generator (by getting all the used keys) ?

My main goal is to make a generator as generic as possible, not define a custom generator for each model.

Umbardacil
  • 31
  • 4

2 Answers2

0

An AutoField [Django-doc] is, like the documentation says:

An IntegerField that automatically increments according to available IDs. You usually won't need to use this directly; a primary key field will automatically be added to your model if you don’t specify otherwise.

It is thus an IntegerField [Django-doc], and we thus can simply use an IntegerField instead, llike:

class SomeModel(models.Model):
    pk = models.IntegerField(editable=False, auto_created=True, primary_key=True, verbose_name='ID', default=genkey)
    # ...

So specifying a default on an AutoField is a bit in contradiction with what an AutoField does. If you make an IntegerField with editable=False [Django-doc], to ensure that, at least by default, it will not be included in a ModelForm. This is in essence what a primary key does.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Why does it need to be blank=True? The default is set on instantiation, so the field will never be blank. – Daniel Roseman Jun 28 '19 at 11:13
  • @DanielRoseman: you don't need to set it `blank=True`, but the `AutoField` sets it blank (https://github.com/django/django/blob/master/django/db/models/fields/__init__.py#L906). Likely because otherwise it will apear in a `ModelForm`, whereas a pk usually is not something a user is supposed to create/update. – Willem Van Onsem Jun 28 '19 at 11:15
  • No, that's because as you say AutoField doesn't have a default, it's set by the db on save, so if blank is False then the model will never be valid. That's not the case here, where we do have a default. – Daniel Roseman Jun 28 '19 at 11:21
  • @DanielRoseman: hmmm... yes. Perhaps `editable=False` is better then? – Willem Van Onsem Jun 28 '19 at 11:23
0

After making some research and tests, I ended up creating a class.

class RandomIDKey(models.Model):
    id = models.IntegerField(
        auto_created=True,
        primary_key=True,
        serialize=False,
        verbose_name='ID',
        unique=True,
        db_index=True,
        editable=False
    )

    def save(self, *args, **kwargs):
        if not self.id:
            self.id = genkey(type(self))
        super().save(*args, **kwargs)

    class Meta:
        abstract = True

with a public function:

def genkey(model):
    generated_key = random.randrange(1, 2147483648)
    if model.objects.filter(pk=generated_key).exists():
        generated_key = genkey(model)
    return generated_key

Using an AutoField is a bad idea since the field is not auto-generated by the database, hence the IntegerField

Umbardacil
  • 31
  • 4