﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
11265	ForeignKey/OneToOneField should support user-defined id attribute name	dstora	nobody	"Currently, when defining a !ForeignKey/OneToOneField field XXX, Django automatically defines a XXX_id attribute to store the id value.[[BR]]
However, it is sometimes desirable to decide of the name as XXX_id may not be the most appropriate.

----

Let's take the following example:

- I initially have a table ""Payment"" with a ""ccy_code"" field designating a 3-letter currency code.
My model definition looks like:
{{{
    from django.db import model
    class Payment(models.Model):
        # ...
        ccy_code = models.CharField(max_length=3)

}}}
In my python code I refer to the currency code as follows:
{{{
    p = Payment()
    #...
    print p.ccy_code

}}}

- Later, I decide to actually create a ""Currency"" table to hold some information about currencies.
And I also decide to define a foreign key constraint from ""Payment"" to ""Currency"".
My model now looks like:
{{{
    from django.db import model
    class Currency(models.Model):
        ccy_code = models.CharField(max_length=3, primary_key=True)
        #...
    class Payment(models.Model):
        # ...
        ccy = models.ForeignKey(Currency, to_field=""ccy_code"", db_column=""ccy_code"")

}}}
The problem here is that my existing Python code is broken, because ""ccy_code"" is not defined anymore for ""Payment"".
Django has instead define a ""ccy_id"" attribute.

----

Based on the principle that defining things like foreign keys should only add functionality (and not remove any) it seems quite important to me that one can choose the id attribute name.[[BR]]
[[BR]]
This can be achieved by adding an optional keyword argument to !ForeignKey/OneToOneField constructors (here I decided to call it ""id_attr_name"").[[BR]]
The implementation of this feature is pretty small and fully backward-compatible.[[BR]]
Here is the SVN diff against trunk:
{{{
Index: related.py
===================================================================
--- related.py  (revision 10924)
+++ related.py  (working copy)
@@ -660,6 +660,7 @@
 class ForeignKey(RelatedField, Field):
     empty_strings_allowed = False
     def __init__(self, to, to_field=None, rel_class=ManyToOneRel, **kwargs):
+        self.__id_attr_name = kwargs.pop('id_attr_name', None)
         try:
             to_name = to._meta.object_name.lower()
         except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
@@ -679,7 +680,10 @@
         self.db_index = True

     def get_attname(self):
-        return '%s_id' % self.name
+        if self.__id_attr_name:
+            return self.__id_attr_name
+        else:
+            return '%s_id' % self.name

     def get_validator_unique_lookup_type(self):
         return '%s__%s__exact' % (self.name, self.rel.get_related_field().name)

}}}

In my example, I could then define my model like:
{{{
    from django.db import model
    class Currency(models.Model):
        ccy_code = models.CharField(max_length=3, primary_key=True)
        #...
    class Payment(models.Model):
        # ...
        ccy = models.ForeignKey(Currency, to_field=""ccy_code"", db_column=""ccy_code"", id_attr_name=""ccy_code"")

}}}
"	New feature	new	Database layer (models, ORM)		Normal		foreign key ForeignKey OneToOneField id	cmawebsite@… elonzh	Accepted	1	1	1	0	0	0
