Opened 6 years ago

Closed 4 years ago

#9625 closed (duplicate)

ForeignKey data type for certain derived model fields not calculated correctly

Reported by: robbyd Owned by: nobody
Component: Database layer (models, ORM) Version: 1.0
Severity: Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

I have a case where I want to make a custom "BigAutoField" model field. So, given newforms,
I do something like this in my own code:

class BigAutoField(fields.AutoField):
    empty_strings_allowed=False
    def get_internal_type(self):
        return "BigAutoField"
    
    def db_type(self):
        assert settings.DATABASE_ENGINE == 'mysql'
        return 'bigint UNSIGNED AUTO_INCREMENT'


I then change the appropriate parts of my model to use this BigAutoField in place
of Django's AutoField. Such as:

class Foo(models.Model):
    id = custom_model_fields.BigAutoField('ID', primary_key=True)

Then, I have Django generate the schema and populate the DB. Looking at the data type of this
'Foo' table, I see that ID is UNSIGNED BIGINT. Great. However, when I look at the data type
of any foreign keys that reference it, I see that they are still INTEGER.

So I went and looked at the SVN code. In django/db/models/fields/related.py around line 701,
I saw:

    def db_type(self):
        # The database column type of a ForeignKey is the column type
        # of the field to which it points. An exception is if the ForeignKey
        # points to an AutoField/PositiveIntegerField/PositiveSmallIntegerField,
        # in which case the column type is simply that of an IntegerField.
        # If the database needs similar types for key fields however, the only
        # thing we can do is making AutoField an IntegerField.
        rel_field = self.rel.get_related_field()
        if (isinstance(rel_field, AutoField) or
                (not connection.features.related_fields_match_type and
                isinstance(rel_field, (PositiveIntegerField,
                                       PositiveSmallIntegerField)))):
            return IntegerField().db_type()
        return rel_field.db_type()

So it looks like my model is falling into the trap of isinstance(AutoField) being True,
since it is derived from AutoField. Now the quick and dirty hack around this is to copy
AutoField from django/db/models/fields/init.py and make that the basis of my BigAutoField.
I can do that for now, but in the long run I'd like to avoid putting internal Django code
into my project.

So, what do you guys recommend happen here? I see this code in Django
I referenced being a problem for anyone that tries to make their own model fields that
derive from AutoField, PositiveIntegerField and PositiveSmallIntegerField.

Change History (3)

comment:1 Changed 6 years ago by jacob

  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

comment:2 Changed 4 years ago by hartror

The other problem with the above example is if you don't derive from AutoField (which I didn't) then the ForeignKey will get the the rel_field.db_type() as it's own which in this case will be 'bigint UNSIGNED AUTO_INCREMENT'. This of course is invalid being that the ForeignKey can't auto increment. My solution has been to write the CREATE TABLE SQL myself.

This of course is the easy MySQL example, it gets even more fun when we start to deal with the other 3 databases that Django supports.

comment:3 Changed 4 years ago by mmcnickle

  • Resolution set to duplicate
  • Status changed from new to closed

Duplicate of #13774.

Closing this bug, even though it was reported first, as the other ticket has a workable patch.

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