管理员内联没有ForeignKey关系

Fla*_*ash 8 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, )
Run Code Online (Sandbox Code Playgroud)

我希望我的Diary模型管理员显示一个内联Sleep模型,该模型具有start_time与之相同的日期Diary.day.问题是该Sleep模型没有ForeignKeyto Diary(相反,该关系是由使用日期隐含的).

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

<class 'records.admin.SleepInline'>: (admin.E202) 'records.Sleep' has no ForeignKey to 'records.Diary'.
Run Code Online (Sandbox Code Playgroud)

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

Mel*_*vyn 5

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

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

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

但是……管理员使用了 ModelForm。所以,如果你构建一个形式的formset(不能是出于同样的原因一个内联表单集)和手柄自己保存该表单集,它应该是可能的。InlineFormset 和 InlineAdmin 的重点是使从相关模型生成表单集变得更容易,因此它需要知道关系。

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


小智 5

有回避的事实,Django管理的内嵌周围建有没有得到ForeignKey字段(或ManyToManyFieldOneToOneField)。但是,如果我了解您的目标,那就是避免在您Diary.daySleep.start_time字段之间管理“日期完整性” ,即,当外键关系真正由以下项定义时,Diary.day == Sleep.start_time.date()

Django ForiegnKey字段具有to_field属性,该属性允许FK索引除了之外的列id。但是,由于您有DateTimeFieldin SleepDateFieldin Diary,我们需要将其拆分DateTimeField。同样,a ForeignKey必须与关系的“ 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()
Run Code Online (Sandbox Code Playgroud)

然后你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, )
Run Code Online (Sandbox Code Playgroud)

即使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')
Run Code Online (Sandbox Code Playgroud)

一个人想写点东西

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

但是,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的管理员一起使用。