Opened 16 years ago

Closed 16 years ago

Last modified 12 years ago

#7918 closed (fixed)

Allow ForeignKey to a parent class when using inlines

Reported by: sil Owned by: Brian Rosner
Component: Database layer (models, ORM) Version: dev
Severity: Keywords:
Cc: arnaud.rebts@… Triage Stage: Accepted
Has patch: yes Needs documentation: yes
Needs tests: yes Patch needs improvement: no
Easy pickings: no UI/UX: no

Description (last modified by Brian Rosner)

At the moment, if you have a hierarchy:

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

(as per http://www.djangoproject.com/documentation/model-api/#multi-table-inheritance), it doesn't interact all that well with inlined ForeignKeyed models. Imagine you had:

class Owner:
    name = models.CharField(max_length=100)
    place = models.ForeignKey(Place)

(so a Place can have multiple Owners). This also works fine with Restaurants, because every Restaurant is-a Place. However, if you want to inline the Owners:

class OwnerInline(admin.TabularInline): 
    model = Owner
    extra = 5

class PlaceAdmin(admin.ModelAdmin):
    model = Place
    inlines = [OwnerInline]

then this won't work; if you try and create a Restaurant, you get a complaint that Owner has no ForeignKey to Restaurant (because technically it doesn't). This is because (new)forms._get_foreign_key checks that the ForeignKey points to this model itself, where it should really be checking whether the ForeignKey points to this model or any of its ancestor classes. The attached patch rectifies this.

Attachments (1)

subclass-foreignkey.patch (1.3 KB ) - added by sil 16 years ago.
Updated to handle fail

Download all attachments as: .zip

Change History (16)

comment:1 by sil, 16 years ago

mrph. Code from above in code formatting. Sorry.

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField?()

class Owner:
    name = models.CharField(max_length=100)
    place = models.ForeignKey(Place)

class OwnerInline(admin.TabularInline):
    model = Owner
    extra = 5

class PlaceAdmin(admin.ModelAdmin):
    model = Place
    inlines = [OwnerInline]

comment:2 by sil, 16 years ago

Needs documentation: set
Needs tests: set
Patch needs improvement: set

comment:3 by Brian Rosner, 16 years ago

Description: modified (diff)

Fixed ticket description formatting.

comment:4 by Brian Rosner, 16 years ago

Owner: changed from nobody to Brian Rosner
Status: newassigned
Triage Stage: UnreviewedAccepted
Version: newforms-adminSVN

comment:5 by Brian Rosner, 16 years ago

milestone: 1.0 beta

by sil, 16 years ago

Attachment: subclass-foreignkey.patch added

Updated to handle fail

comment:6 by sil, 16 years ago

Updated patch to handle the OneToOneField value being null when you're first adding a Restaurant, which blows up because it's not a nullable field (since it's a PK) but doesn't have a value (because the Restaurant isn't saved yet). Sorry.

comment:7 by sil, 16 years ago

Patch needs improvement: unset
Triage Stage: AcceptedUnreviewed

comment:8 by Brian Rosner, 16 years ago

Triage Stage: UnreviewedAccepted

Don't change the stage. That is reserved for ticket triagers and committers. The ticket itself is accepted, you don't need to do that when you upload a new patch. The needs_better_patch is meant for that :)

comment:9 by sil, 16 years ago

Cool -- I figured that the newer patch wasn't reviewed :) no problem!

comment:10 by Brian Rosner, 16 years ago

It is likely [8165] has fixed this. Can someone verify? Perhaps a test case should be ordered :)

comment:11 by Brian Rosner, 16 years ago

milestone: 1.0 beta1.0

comment:12 by Arnaud Rebts, 16 years ago

Cc: arnaud.rebts@… added

It seems to be working now if the ForeinKey is in the direct super class, but not if it's in a higher class.

So, this is working:

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

class Restaurant(Place):
    serves_hot_dogs = models.BooleanField()
    serves_pizza = models.BooleanField()

class Owner(models.Model):
    name = models.CharField(max_length=100)
    place = models.ForeignKey(Place)

class OwnerInline(admin.TabularInline): 
    model = Owner
    extra = 5

class PlaceAdmin(admin.ModelAdmin):
    model = Place
    inlines = [OwnerInline]

class RestaurantAdmin(admin.ModelAdmin):
    model = Restaurant
    inlines = [OwnerInline]

But not this:

class ItalianRestaurant(Restaurant):
    pass

class ItalianRestaurantAdmin(admin.ModelAdmin):
    model = ItalianRestaurant
    inlines = [OwnerInline]

comment:13 by Jacob, 16 years ago

Component: UncategorizedDatabase wrapper

comment:14 by Brian Rosner, 16 years ago

Resolution: fixed
Status: assignedclosed

(In [8708]) Fixed #7918 -- Allow the foreign key in an inline to be any where in the parent chain. Thanks sil for the report.

comment:15 by Jacob, 12 years ago

milestone: 1.0

Milestone 1.0 deleted

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