7đź‘Ť
Let me start by showing you the drawbacks of your logic:
- When adding a foreign key, there are 2 operations, that are uncommon that require adjusting the relation: creating a new sleep object and updating the times on the sleep object.
- When not using a foreign key, each time a diary is requested the lookup for the corresponding Sleep object(s) needs to be done. I’m assuming reading diaries is much more common then alterations of sleep objects, as it will be in most projects out there.
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.
8đź‘Ť
There is no getting around the fact that Django admin inlines are built around ForeignKey
fields (or ManyToManyField
, OneToOneField
). However, if I understand your goal, it’s to avoid having to manage “date integrity” between your Diary.day
and Sleep.start_time
fields, i.e., the redundancy in a foreign key relation when that relation is really defined by Diary.day == Sleep.start_time.date()
A Django ForiegnKey
field has a to_field property that allows the FK to index a column besides id
. However, as you have a DateTimeField
in Sleep
and a DateField
in Diary
, we’ll need to split that DateTimeField
up. Also, a ForeignKey
has to relate to something unique on the “1” side of the relation. Diary.day
needs to be set unique=True
.
In this approach, your models look like
from django.db import models
# Parent
class Diary(models.Model):
day = models.DateField(unique=True)
activities = models.TextField()
# Child
class Sleep(models.Model):
diary = models.ForeignKey(Diary, to_field='day', on_delete=models.CASCADE)
start_time = models.TimeField()
end_time = models.DateTimeField()
and then your admin.py
is just
from django.contrib import admin
from .models import Sleep, Diary
class SleepInline(admin.TabularInline):
model=Sleep
@admin.register(Diary)
class DiaryAdmin(admin.ModelAdmin):
inlines = (SleepInline, )
Even 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:
class Diary(models.Model):
user = models.ForeignKey(User)
day = models.DateField()
activities = models.TextField()
class Meta:
unique_together = ('user', 'day')
One would like to write something like
class Sleep(models.Model):
diary = models.ForeignKey(Diary, to_fields=['user', 'day'], on_delete=models.CASCADE)
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.
- Is there a way to combine behavior of SESSION_EXPIRE_AT_BROWSER_CLOSE and SESSION_COOKIE_AGE
- ValueError: "needs to have a value for field "id" before this many-to-many relationship can be used"
- How to create a Django superuser if it doesn't exist non-interactively?
0đź‘Ť
You can achieve this, using nested_admin library.
So in myapp/admin.py
:
from nested_admin.nested import NestedStackedInline, NestedModelAdmin
class SleepInline(NestedStackedInline):
model = Sleep
class DiaryAdmin(NestedModelAdmin):
inlines = [SleepInline]
- Django 1.6: How to access static files in view
- How to implement a paginator that doesn't call count(*)
- Creating a .gitignore file for a Django website