Django admin - OneToOneField inline throws"没有ForeignKey"例外

时间:2014-04-26 01:01:09

标签: python django django-forms django-admin

我有一个非常简单的应用程序,目前宣布两个模型:一个被称为"内容"并简单地保存内容数据,另一个是" Page"其中包括"内容"作为OneToOneField。

我之所以做到这一点,是因为我可以拥有" Page"作为我使用的实际具体类,当我计划的其他模块中的其他模型需要页面数据时,它们可以简单地包括"内容"作为OneToOneField。我已经这样做了,以避免继承和使用组合。

models.py:

from django.db import models

class Content(models.Model):
    """Basic page data which can be used by other modules"""
    title = models.CharField(max_length=200)
    html_title = models.CharField(max_length=200)
    meta_desc = models.CharField(max_length=200)
    keywords = models.CharField(max_length=200)
    content = models.TextField()

class Page(models.Model):
    """Concrete implementation of a basic page managed by the admin"""
    slug = models.SlugField()
    content = models.OneToOneField(Content)

    def __str__(self):
        return self.content.title

admin.py:

from django.contrib import admin
from content.models import Page, Content

class ContentInline(admin.TabularInline):
    model = Content
    fields = ('title', 'html_title', 'meta_desc', 'keywords', 'content')

class PageAdmin(admin.ModelAdmin):
    fields = ('slug',)
    inlines = [ContentInline]

在页面管理员上我得到了这个例外:

Exception at /admin/content/page/add/
<class 'content.models.Content'> has no ForeignKey to <class 'content.models.Page'>

说什么当然是正确的,但我似乎无法找到一种方法来做我想要的,包括内联关系的非定义方。我不想在&#34; Content&#34;上宣布这种关系。因为那时我必须在该类中定义它的每一个关系,这会将依赖关系引入其他模块,在我看来它应该一无所知。

在Python 3.3上使用Django 1.6。

编辑如评论中所示,我决定使用继承。我最初担心的是,我希望能够灵活地从多个其他类组成类。但是,由于Django ORM确实支持多重继承,并且如果我意识到该方法被称为&#34; mixins&#34; (Python新手)我会早点到达某个地方。

示例mixins与模型:

from django.db import models

class Content(models.Model):
    """Basic page data which can be used by other modules"""
    title = models.CharField(max_length=200)
    html_title = models.CharField(max_length=200)
    meta_desc = models.CharField(max_length=200)
    keywords = models.CharField(max_length=200)
    content = models.TextField()

    def __str__(self):
        return self.title

    class Meta:
        abstract = True

class Data(models.Model):
    data_name = models.CharField(max_length=200)

    class Meta:
        abstract = True

class Page(Content, Data):
    """Concrete implementation of a basic page managed by the admin"""
    slug = models.SlugField()

然后我可以在admin.py中将其用作一个模型。

2 个答案:

答案 0 :(得分:0)

另一种解决方案是将OneToOneFieldContent移至Page

class Content(models.Model):
    """Basic page data which can be used by other modules"""
    title = models.CharField(max_length=200)
    html_title = models.CharField(max_length=200)
    meta_desc = models.CharField(max_length=200)
    keywords = models.CharField(max_length=200)
    content = models.TextField()
    page = models.OneToOneField(Page, primary_key=True, related_name="content")

class Page(models.Model):
    """Concrete implementation of a basic page managed by the admin"""
    slug = models.SlugField()

    def __str__(self):
        return self.content.title

您仍然可以执行page.content,内联表单将开箱即用

编辑:

该方法的一个缺点是它允许用户创建页面而不向其分配任何内容(在这种情况下page.content将崩溃)

通过创建自定义表单

很容易克服这个问题
class ContentAdminForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        kwargs["empty_permitted"] = False
        super(ContentAdminForm, self).__init__(*args, **kwargs)

然后在管理页面

class ContentInline(admin.TabularInline):

    model = Content
    form = ContentAdminForm
    fields = ('title', 'html_title', 'meta_desc', 'keywords', 'content')

答案 1 :(得分:-1)

如果您根本不想更改模型,可以使用django模块显示非定义边内联:django_reverse_admin

您需要将django_reverse_admin添加到您的requirements.txt:

-e git+https://github.com/anziem/django_reverse_admin.git#egg=django_reverse_admin

然后导入它:

admin.py

from django.contrib import admin
from django_reverse_admin import ReverseModelAdmin

from content.models import Page, Content

# don't need to define an inline anymore for Content

class PageAdmin(ReverseModelAdmin):
    fields = ('slug',)

    inline_reverse = ['content']
    inline_type = 'tabular'  # or could be 'stacked'