Is it possible to manually specify the set of related object to show in an inline, where no foreign key relation exists?
# Parent
class Diary(models.Model):
day = models.DateField()
activities = models.TextField()
# Child
class Sleep(models.Model):
start_time = models.DateTimeField()
end_time = models.DateTimeField()
class SleepInline(admin.TabularInline):
model=Sleep
def get_queryset(self, request):
# Return all Sleep objects where start_time and end_time are within Diary.day
return Sleep.objects.filter(XXX)
class DiaryAdmin(admin.ModelAdmin):
inlines = (SleepInline, )
I want my Diary
model admin to display an inline for Sleep
models that have start_time
equal to the same day as Diary.day
. The problem is that the Sleep
model does not have a ForeignKey
to Diary
(instead, the relation is implicit by the use of dates).
Using the above, Django immediately complains that
<class 'records.admin.SleepInline'>: (admin.E202) 'records.Sleep' has no ForeignKey to 'records.Diary'.
How can I show the relevant Sleep
instances as inlines on the Diary
admin page?
Let me start by showing you the drawbacks of your logic:
The additional drawback as you've noticed, is that you cannot use relational features. InlineAdmin is a relational feature, so as much as you say "making the admin work", it is really that you demand a hammer to unscrew a bolt.
But...the admin makes use of ModelForm. So if you construct the form with a formset (which cannot be a an inline formset for the same reason) and handle saving that formset yourself, it should be possible. The whole point of InlineFormset and InlineAdmin is to make generation of formsets from related models easier and for that it needs to know the relation.
And finally, you can add urls and build a custom page, and when extending the admin/base.html template, you will have access to the layout and javascript components.
There is no getting around the fact that Django admin inlines are built around
ForeignKey
fields (orManyToManyField
,OneToOneField
). However, if I understand your goal, it's to avoid having to manage "date integrity" between yourDiary.day
andSleep.start_time
fields, i.e., the redundancy in a foreign key relation when that relation is really defined byDiary.day == Sleep.start_time.date()
A Django
ForiegnKey
field has a to_field property that allows the FK to index a column besidesid
. However, as you have aDateTimeField
inSleep
and aDateField
inDiary
, we'll need to split thatDateTimeField
up. Also, aForeignKey
has to relate to something unique on the "1" side of the relation.Diary.day
needs to be setunique=True
.In this approach, your models look like
and then your
admin.py
is justEven though
Sleep.start_time
no longer has a date, the Django Admin is quite what you'd expect, and avoids "date redundancy":Thinking ahead to a more real (and problematic) use case, say every user can have 1 Diary per day:
One would like to write something like
However, there's no such feature in Django 1.11, nor can I find any serious discussion of adding that. Certainly composite foreign keys are allowed in Postgres and other SQL DBMS's. I get the impression from the Django source they're keeping their options open: https://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related.py#L621 hints at a future implementation.
Finally, https://pypi.python.org/pypi/django-composite-foreignkey looks interesting at first, but doesn't create "real" composite foreign keys, nor does it work with Django's admin.