How do I define a default (override-able) primary key field on an abstract model?

646 views
Skip to first unread message

g

unread,
Nov 5, 2011, 6:19:54 PM11/5/11
to Django users
How do I define a custom primary key field on Base that can be
overridden by B without an error?

class Base(Model):
class Meta:
abstract = True
id = SomeAwesomeIDField(primary_key=True)
other_common_field = AwesomeField()

class A(Base):
pass

class B(Base):
a = OneToOneField(A,primary_key=True)



django.db.utils.DatabaseError: table "..." has more than one primary
key

Kurtis Mullins

unread,
Nov 5, 2011, 6:48:24 PM11/5/11
to django...@googlegroups.com
I don't think the problem is overriding the Primary Key. I could be wrong, but I think the actual problem here is that you can not use OneToOneFields, ManyToManyFields, or ForeignKeys as primary keys. I'm not sure how it would work on a database level. Like I said, I could be wrong -- but that's my initial hunch. Try overriding it with something else like an IntegerField and see if you still get an error.


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.


Furbee

unread,
Nov 5, 2011, 6:53:41 PM11/5/11
to django...@googlegroups.com
I can't speak to the reference field being used as a primary key, but the initial error is that you have multiple primary keys on the single model B. If it 'is-a' class Base, it inherits id as a primary key, and also defines a and a primary key. You cannot have multiple primary key fieds, hence "table B has more than one primary key."

Furbeenator

Kurtis Mullins

unread,
Nov 5, 2011, 7:02:36 PM11/5/11
to django...@googlegroups.com
Good point, Furbee!

"g", I was going to suggest just overriding that "id" field -- but I read the docs real quick and they say:

"It is an error to have fields in the abstract base class with the same name as those in the child (and Django will raise an exception)."

So yeah, the initial error is definitely that you're trying to have multiple primary keys for a single table (Model). In the end, you might also have problems trying to use a relationship as a Primary key as well.

g

unread,
Nov 5, 2011, 9:05:20 PM11/5/11
to Django users
I do not guess I was clear with my problem, so I am going to try to
state it more clearly.

I know that I am defining two primary keys and this is the problem.
Using a OneToOneField as a primary key is fine. However, I want to
set up the base abstract model to *only* define the primary key (id =
SomeAwesomeIDField(primary_key=True)) if the inheriting class does not
define a primary key... similar to the way that the default model
does with an AutoField. I do not want subclasses of my base class to
use the auto increment primary key by default.

I used different names for the primary keys because I was trying to
show that I did not want to force the primary key to be named "id".
Rather, I want to check for any primary key already present on the
class. Hence the use of "a" and a OneToOneField.

For example, one solution to the problem would be to use two abstract
base classes. One that defines common fields, and one that defines
the id. And then only use both abstract classes for model A and only
common fields for model B.

However, I would prefer to only have one base class. As such, I am
looking to implement an automatic id field similar to how
django.db.models.Model does it. The problem is that after looking at
the source code, I am not sure where this takes place.

So, is it possible to conditionally define model fields in an abstract
model base class depending on the fields defined in the subclass?

If not, why? Where and/or how does this automatic field get assigned
when using models.Model?

Thanks



On Nov 5, 7:02 pm, Kurtis Mullins <kurtis.mull...@gmail.com> wrote:
> Good point, Furbee!
>
> "g", I was going to suggest just overriding that "id" field -- but I read
> the docs real quick and they say:
>
> "It is an error to have fields in the abstract base class with the same
> name as those in the child (and Django will raise an exception)."https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base...
>
> So yeah, the initial error is definitely that you're trying to have
> multiple primary keys for a single table (Model). In the end, you might
> also have problems trying to use a relationship as a Primary key as well.
>
>
>
>
>
>
>
> On Sat, Nov 5, 2011 at 6:53 PM, Furbee <Furbeena...@gmail.com> wrote:
> > I can't speak to the reference field being used as a primary key, but the
> > initial error is that you have multiple primary keys on the single model B.
> > If it 'is-a' class Base, it inherits id as a primary key, and also defines
> > a and a primary key. You cannot have multiple primary key fieds, hence
> > "table B has more than one primary key."
>
> > Furbeenator
>
> > On Sat, Nov 5, 2011 at 3:48 PM, Kurtis Mullins <kurtis.mull...@gmail.com>wrote:
>
> >> I don't think the problem is overriding the Primary Key. I could be
> >> wrong, but I think the actual problem here is that you can not use
> >> OneToOneFields, ManyToManyFields, or ForeignKeys as primary keys. I'm not
> >> sure how it would work on a database level. Like I said, I could be wrong
> >> -- but that's my initial hunch. Try overriding it with something else like
> >> an IntegerField and see if you still get an error.
>

g

unread,
Nov 6, 2011, 3:15:42 PM11/6/11
to Django users
I have come up with a way to do this below. Is there a better way?
Obviously the CharField is just for demonstration. The real field
would need to create a usable id. Here is what I have so far:

from django.db.models import Model, CharField
from django.db.models.base import ModelBase

class Base(ModelBase):
def _prepare(cls):
needs_primary_key = True
for field in cls._meta.fields:
if field.primary_key:
needs_primary_key = False
break
if needs_primary_key:
auto = CharField(verbose_name='ID', primary_key=True,
max_length=255)
cls.add_to_class('id', auto)
super(Base,cls)._prepare()

class CustomAutoID(Model):
__metaclass__ = Base
class Meta:
abstract = True



Reply all
Reply to author
Forward
0 new messages