管理员内联没有ForeignKey关系

时间:2017-06-20 07:33:08

标签: django python-3.x django-1.11

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

# 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模型管理员显示Sleep模型的内联,其中start_time等于与Diary.day相同的日期。问题是Sleep模型没有ForeignKeyDiary(相反,使用日期隐含了这种关系)。

使用上面的内容,Django立即抱怨

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

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

3 个答案:

答案 0 :(得分:5)

Django管理员内联是围绕ForeignKey字段(或ManyToManyFieldOneToOneField)构建的。但是,如果我了解您的目标,就必须避免必须管理&#34;日期完整性&#34;您的Diary.daySleep.start_time字段之间的关系,即当该关系确实由Diary.day == Sleep.start_time.date()

定义时外键关系中的冗余

Django ForiegnKey字段有to_field property,允许FK索引除id之外的列。但是,由于DateTimeField中有SleepDateField中有Diary,我们需要将DateTimeField分开。此外,ForeignKey必须与&#34; 1&#34;关系的一面。需要设置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管理员也是您期望的,并且避免了&#34;日期冗余&#34;:

Django Admin: Diary

提前考虑更真实(和有问题的)用例,假设每个用户每天可以有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一开始看起来很有趣,但并没有创造出真正的&#34;复合外键,也不适用于Django的管理员。

答案 1 :(得分:2)

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

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

您注意到的另一个缺点是您无法使用关系功能。 InlineAdmin是一个关系功能,所以就像你说“让管理工作”一样,你真的要求用锤子拧开螺栓。

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

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

答案 2 :(得分:0)

您可以使用Nested_admin库实现此目的。所以myapp / admin.py

from nested_admin.nested import NestedStackedInline, NestedModelAdmin

class SleepInline(NestedStackedInline):

    model = Sleep

class DiaryAdmin(NestedModelAdmin):

   inlines = [SleepInline]