13

是否可以手动指定要在内联中显示的相关对象集,其中不存在外键关系?

# 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, )

我希望我的模型Diary管理员为与. 问题是模型没有to (相反,关系是通过使用日期隐含的)。Sleepstart_timeDiary.daySleepForeignKeyDiary

使用上述内容,Django 立即抱怨说

<class 'records.admin.SleepInline'>: (admin.E202) 'records.Sleep' has no ForeignKey to 'records.Diary'.

如何在管理页面上将相关Sleep实例显示为内联?Diary

4

3 回答 3

8

Django 管理内联是围绕ForeignKey字段(或ManyToManyField, OneToOneField)构建的这一事实无法回避。但是,如果我理解您的目标,那就是避免必须管理您的Diary.daySleep.start_time字段之间的“日期完整性”,即当关系真正定义为时,外键关系中的冗余Diary.day == Sleep.start_time.date()

DjangoForiegnKey字段有一个to_field 属性,允许 FK 索引除id. 但是,由于您有 a DateTimeFieldinSleep和 a DateFieldin Diary,我们需要将其分开DateTimeField。此外,aForeignKey必须与关系的“1”一侧的独特事物相关。Diary.day需要设置unique=True

在这种方法中,您的模型看起来像

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()

然后你admin.py就是

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, )

即使Sleep.start_time不再有日期,Django Admin 也完全符合您的期望,并且避免了“日期冗余”:

Django 管理员:日记


考虑一个更真实(且有问题)的用例,假设每个用户每天可以拥有 1 份日记:

class Diary(models.Model):
    user = models.ForeignKey(User)
    day = models.DateField()
    activities = models.TextField()

    class Meta:
        unique_together = ('user', 'day')

一个人想写类似的东西

class Sleep(models.Model):
    diary = models.ForeignKey(Diary, to_fields=['user', 'day'], on_delete=models.CASCADE)

但是,Django 1.11 中没有这样的功能,我也找不到任何关于添加它的严肃讨论。在 Postgres 和其他 SQL DBMS 中当然允许复合外键。我从 Django 源代码中得到了他们保持选项开放的印象:https ://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related.py#L621提示在未来的实施中。

最后,https: //pypi.python.org/pypi/django-composite-foreignkey 起初看起来很有趣,但它不能创建“真正的”复合外键,也不能与 Django 的管理员一起使用。

于 2017-06-27T03:49:06.343 回答
5

让我首先向您展示您的逻辑的缺点:

  • 添加外键时,有两个不常见的操作需要调整关系:创建新的睡眠对象和更新睡眠对象的时间。
  • 当不使用外键时,每次请求日记时,都需要查找相应的睡眠对象。我假设阅读日记比改变睡眠对象更常见,因为它将在大多数项目中出现。

您注意到的另一个缺点是您不能使用关系功能。InlineAdmin 是一个关系功能,所以正如您所说的“让管理员工作”,实际上您需要一把锤子来拧下螺栓。

但是...管理员使用 ModelForm。因此,如果您使用表单集构建表单出于同样的原因,它不能是内联表单集)并自己处理保存该表单集,它应该是可能的。InlineFormset 和 InlineAdmin 的重点是使从相关模型生成表单集更容易,为此它需要知道关系。

最后,您可以添加 url并构建自定义页面,并且在扩展 admin/base.html 模板时,您将可以访问布局和 javascript 组件。

于 2017-06-24T16:52:53.563 回答
0

您可以使用nested_admin library来实现这一点。

所以在myapp/admin.py

from nested_admin.nested import NestedStackedInline, NestedModelAdmin

class SleepInline(NestedStackedInline):

    model = Sleep

class DiaryAdmin(NestedModelAdmin):

   inlines = [SleepInline]
于 2020-03-31T18:42:07.620 回答