【问题标题】:How to link to a new model instance creation page related to the currently viewed instance如何链接到与当前查看的实例相关的新模型实例创建页面
【发布时间】:2012-03-18 04:44:29
【问题描述】:

我有一些结构与此类似的 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 外键字段。

想到的是内联,但我无法使用它们,因为内联不能嵌套,我需要它们来操作 Parent 页面上 ChildAChildB 内的字段.

【问题讨论】:

  • 嗯...不。这不是我的意思。我更改了一些字段以澄清一些事情。
  • 我的第一个答案就是你需要的。

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


【解决方案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)

【讨论】:

  • 好吧,我无法让它工作......我是 django 的初学者,无法弄清楚答案中的所有内容。当我更有经验时,我会在项目中进一步讨论它。谢谢。
  • 可能不止一个问题,但我认为我在第 4 步中做错了。我无法访问 get('parent_pk')。错误消息说 Nonetype 对象中没有“get”方法。 ChildAdmin 类代码应该放在哪里?无论如何,孩子正在使用内联进行编辑。
  • 不,我的模型是正确的。我希望父母有两种孩子(ChildAChildB),它们从Child继承一些字段,而不是三层子树。
  • 那么,@Ohad,您能否通过上述更新的解决方案解决此问题?
  • 我没有使用您的解决方案,但它与我最终所做的非常相似。我真的很感谢所有的帮助!谢谢。
猜你喜欢
  • 1970-01-01
  • 2018-03-21
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
  • 2019-03-25
  • 2019-10-14
  • 2013-02-11
  • 2019-02-25
相关资源
最近更新 更多