如何链接到与当前查看的实例相关的新模型实例创建页面

时间:2012-02-28 17:16:08

标签: django django-forms django-admin django-widget

我有一些Django模型的结构类似于:

class Grandparent(models.Model):
    name = models.CharField(max_length=80)

class Parent(models.Model):
    name = models.CharField(max_length=80)
    grandparent = models.ForeignKey(Grandparent, related_name='children')

class Child(models.Model):
    parent = models.ForeignKey(Parent, related_name='children')

class ChildA(Child)
    something = models.CharField(max_length=80)
    anotherthing = models.IntegerField()

class ChildB(Child)
    anything = models.CharField(max_length=80)
    hello = models.BooleanField()

所以基本上,有两种孩子从一个实际抽象的Child模型继承而来。 (它不是真的抽象,所以我可以使用外键)。

问题是 - 如何创建从第一个模型(Grandparent)的管理页面到新Parent模型的创建页面的链接? Parent模型必须已经拥有当前查看的Grandparent页面ID填充的grandparent外键字段。

内联是我想到的事情,但我无法使用它们,因为内联不能嵌套,我将需要它们操纵ChildAChildB内的字段Parent页面。

1 个答案:

答案 0 :(得分:1)

答案包括两个步骤:

# your_app_name/admin.py    
from django import forms
from django.utils.safestring import mark_safe
from django.contrib import admin
from django.core.exceptions import ObjectDoesNotExist

# 1. Override `Grandparent`'s and `Parent`'s `ModelAdmin` forms:
#
# Create a widget with a hyperlink to `Parent` admin form
# with `Grandparent`'s primary key as `GET` parameter.
# Place it in `Grandparent`'s `ModelAdmin`:

class AddParentAdminWidget(forms.Widget):

    def __init__(self, obj, attrs=None):
        self.object = obj
        super(AddParentAdminWidget, self).__init__(attrs)

    def render(self, name, value, attrs=None):
        if self.object.pk:
            return mark_safe(
                u"""<a class="button" href="../../../%(app_label)s/%(object_name)s/add/?grandparent_pk=%(object_pk)s">%(linktitle)s</a>
                """ %\
                {
                    'app_label': Parent._meta.app_label,
                    'object_name': Parent._meta.object_name.lower(),
                    'object_pk': self.object.pk,
                    'linktitle': 'Add new Parent instance with prepopulated Grandparent field.',
                    }
            )
        else:
            return mark_safe(u'')

# Add a dummy `add_new_parent_link` field to `Grandparent's` form
# just to be able to replace it with your previously created widget.

class GrandparentAdminForm(forms.ModelForm):
    add_new_parent_link = forms.CharField(label = 'Add new parent instance', required = False)

    def __init__(self, *args, **kwargs):
        super(GrandparentAdminForm, self).__init__(*args, **kwargs)
        # instance is always available, it just does or doesn't have pk.
        self.fields['add_new_parent_link'].widget = AddParentAdminWidget(self.instance)

    class Meta:
        model = Grandparent

class GrandparentAdmin(admin.ModelAdmin):
    form = GrandparentAdminForm

# 2. Set initial data in `Parent`'s `ModelForm`
#
# (do this by accessing the `request` object,
# which is set in `ParentAdmin` code below):

class ParentAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.request = kwargs.pop('request', None)
        super(ParentAdminForm, self).__init__(*args, **kwargs)
        if self.request.GET.get('grandparent_pk', False):
            try:
                grandparent_pk = int(self.request.GET.get('grandparent_pk', ''),)
            except ValueError:
                pass
            else:
                try:
                    Grandparent.objects.get(pk = grandparent_pk)
                except ObjectDoesNotExist:
                    pass
                else:
                    self.initial['grandparent'] = grandparent_pk

    class Meta:
        model = Parent

# Add your children as regular `StackedInline`'s

class ChildInline(admin.StackedInline):
    model = Child

class ChildAInline(admin.StackedInline):
    model = ChildA

class ChildBInline(admin.StackedInline):
    model = ChildB

class ParentAdmin(admin.ModelAdmin):
    form = ParentAdminForm

    def get_form(self, request, obj=None, **kwargs):
        """
        Use a trick to be able to access `request` object in `ParentAdminForm`.
        Yes, unfortunately we need it [http://stackoverflow.com/a/6062628/497056]
        to access `request` object in an admin `ModelForm` (as of `Django` 1.4).
        Hopefully, this will be fixed in newer versions.
        """
        AdminForm = super(ParentAdmin, self).get_form(request, obj, **kwargs)
        class ModelFormMetaClass(AdminForm):
            """
            We need this metaclass
            to be able to access request in a form
            """
            def __new__(cls, *args, **kwargs):
                kwargs['request'] = request
                return AdminForm(*args, **kwargs)

        return ModelFormMetaClass

    inlines = [
        ChildInline,
        ChildAInline,
        ChildBInline,
        ]

admin.site.register(Grandparent, GrandparentAdmin)
admin.site.register(Parent, ParentAdmin)