Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django non primary_key AutoField

We're migrating and making necessary changes to our Oracle database, one major change is that we're adding an UUIDField as primary_key to all models(hidden to the client), and(trying to add) a regular AutoField.

We found that displaying the primary_key directly to our clients wasn't good design, but they also requested an ID field displayed to reference objects more easily, but Django limits this by not allowing AutoField to NOT be the primary_key

Is there a workaround for this issue?

like image 887
Mojimi Avatar asked Dec 19 '16 17:12

Mojimi


People also ask

How does Django AutoField work?

According to documentation, An AutoField is an IntegerField that automatically increments according to available IDs. One usually won't need to use this directly because a primary key field will automatically be added to your model if you don't specify otherwise.

What is Django ForeignKey model?

ForeignKey is a Django ORM field-to-column mapping for creating and working with relationships between tables in relational databases. ForeignKey is defined within the django. db. models. related module but is typically referenced from django.

What is text field in Django?

TextField is a large text field for large-sized text. TextField is generally used for storing paragraphs and all other text data. The default form widget for this field is TextArea.

What is choices in Django?

ChoiceField in Django Forms is a string field, for selecting a particular choice out of a list of available choices. It is used to implement State, Countries etc. like fields for which information is already defined and user has to choose one. It is used for taking text inputs from the user.


2 Answers

Assuming there is no sequence support in the chosen DBMS, a solution is to create a model:

class Counter(models.Model):
    count = models.PositiveIntegerField(default=0)

    @classmethod
    def get_next(cls):
        with transaction.atomic():
            cls.objects.update(count=models.F('count') + 1)
            return cls.objects.values_list('count', flat=True)[0]

and create one instance of it in a data migration. This could have some implications if you're using transaction management, but it's (if your DBMS supports transactions) guaranteed to always return the next number, regardless of how many objects have been there at the start of a transaction and whether any had been deleted.

like image 109
Yuri Shatrov Avatar answered Oct 02 '22 12:10

Yuri Shatrov


What I think could work is using an IntegerField (pretty much what an AutoField uses under the hood), and increment that on the model's first save (before it's ever put into the database).

I wrote an example model to show this below.

from django.db import models

class MyModel(models.Model):

    # This is what you would increment on save
    # Default this to one as a starting point
    display_id = models.IntegerField(default=1)

    # Rest of your model data

    def save(self, *args, **kwargs):
        # This means that the model isn't saved to the database yet
        if self._state.adding:
            # Get the maximum display_id value from the database
            last_id = self.objects.all().aggregate(largest=models.Max('display_id'))['largest']

            # aggregate can return None! Check it first.
            # If it isn't none, just use the last ID specified (which should be the greatest) and add one to it
            if last_id is not None:
                self.display_id = last_id + 1

        super(MyModel, self).save(*args, **kwargs)

This, in theory, just replicates what AutoField does, just with a different model field.

like image 32
tinfoilboy Avatar answered Oct 02 '22 14:10

tinfoilboy