Opened 9 years ago

Closed 9 years ago

Last modified 9 years ago

#25343 closed Bug (wontfix)

IntegerField(primary_key=True) behaves like AutoField in SQLite but Django doesn't treat it as such

Reported by: eng. Ilian Iliev Owned by: nobody
Component: Database layer (models, ORM) Version: 1.8
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by eng. Ilian Iliev)

Example:

# models.py
class Language(models.Model):
    language = models.IntegerField(primary_key=True)
    code = models.CharField(verbose_name='Language code (2 chars)', blank=False, max_length=2)

# views.py
l = Language.objects.create(code='x')
print l.pk  # return None

The above mention problem happens only if we have "primary_key=True" otherwise it works as expected.

Change History (11)

comment:1 by eng. Ilian Iliev, 9 years ago

Description: modified (diff)

comment:2 by Tim Graham, 9 years ago

Severity: Release blockerNormal

What is the expected behavior? I wouldn't expect an IntegerField to automatically generate a primary key value.

comment:3 by eng. Ilian Iliev, 9 years ago

The expected behaviour is that the pk property to be set to the value of the primary key.

Because if you do like this

lang = Language.objects.create(code='x')
lang.pk is None  # returns True

lang = Language.objects.get(code='x')
lang.pk is not None  # for example 1

Then you have quite inconsistent behaviour

comment:4 by Carl Meyer, 9 years ago

Is this really your exact code? Unless you have null=True on the language field, that code should raise a database error like "null value not allowed in column 'language'".

But it looks like your database is automatically setting a value for that field instead of raising an error. So maybe your database schema doesn't match your model definition? What database are you using, and what is the actual schema for that table? Did Django create it, or did you create it manually?

If you set a value for the language field, that will be the value of the pk attribute.

If you don't want to have to explicitly set a value for the primary key, you should be using AutoField, not IntegerField (or just let Django automatically add an AutoField named id, as it does if you don't set primary_key=True on any of your fields).

in reply to:  4 comment:5 by eng. Ilian Iliev, 9 years ago

Replying to carljm:

Is this really your exact code? Unless you have null=True on the language field, that code should raise a database error like "null value not allowed in column 'language'".

But it looks like your database is automatically setting a value for that field instead of raising an error. So maybe your database schema doesn't match your model definition? What database are you using, and what is the actual schema for that table? Did Django create it, or did you create it manually?

If you set a value for the language field, that will be the value of the pk attribute.

If you don't want to have to explicitly set a value for the primary key, you should be using AutoField, not IntegerField (or just let Django automatically add an AutoField named id, as it does if you don't set primary_key=True on any of your fields).

No actually the exact code is:

class Language(models.Model):
    language = models.IntegerField(verbose_name='Language', primary_key=True) #, choices=LANGUAGES, max_length=2)
    code = models.CharField(verbose_name='Language code (2 chars)', blank=False, max_length=2)
    priority = models.PositiveIntegerField(verbose_name="Priority", default=0, null=False, blank=True)

it is a legacy code, and I am not sure how the DB has been created but I can reproduce it if I recreate the table with syncdb in a new DB (sqlite). The value is automatically autoincremented.
I also found the the language property is also not set in the object after create. However after getting the object from the DB everything is fine.

comment:6 by Tim Graham, 9 years ago

It looks like AutoField() and IntegerField(primary_key=True) create the same "integer PRIMARY KEY" type on SQLite, but Django doesn't know to treat the latter definition as an AutoField (and I don't think it should try to). Other databases (tested with PostgreSQL) with throw the expected IntegrityError for a null value in the language column.

We could document the issue or add a database-specific check when SQLite is in use. Is it worthwhile?

in reply to:  6 comment:7 by eng. Ilian Iliev, 9 years ago

Replying to timgraham:

It looks like AutoField() and IntegerField(primary_key=True) create the same "integer PRIMARY KEY" type on SQLite, but Django doesn't know to treat the latter definition as an AutoField (and I don't think it should try to). Other databases (tested with PostgreSQL) with throw the expected IntegrityError for a null value in the language column.

We could document the issue or add a database-specific check when SQLite is in use. Is it worthwhile?

That makes sense. Regarding whether it should be documented or not - it is a strange corner case caused by ugly legacy code that I stumbled upon so probably this is quite a rare case and there is no need to add it to the docs. It will stay documented here if anyone hits the same issue.

comment:8 by Tim Graham, 9 years ago

Resolution: wontfix
Status: newclosed
Summary: pk attribute not set when having own primary keyIntegerField(primary_key=True) behaves like AutoField in SQLite but Django doesn't treat it as such

Okay, thanks for the feedback

comment:9 by Carl Meyer, 9 years ago

For future reference, in case that decision ever changes (and frankly, I wouldn't turn down a solid patch for this if someone supplied one), the way to fix this would be with a db backend feature flag, I think; something like integer_pk_always_autoincrements.

comment:10 by Tim Graham, 9 years ago

For what it's worth, I don't know if 'autoincrements' in the feature name might be slightly misleading as the default behavior for SQLite is merely to select as unused id. Django does add AUTOINCREMENT for AutoField, so what it generates for the two definitions is actually not strictly equivalent in that respect.

comment:11 by Carl Meyer, 9 years ago

Good point. Something like integer_pk_always_autofills might be better.

Note: See TracTickets for help on using tickets.
Back to Top