Django Admin:如何根据父模型'预填充'内联外键字段

时间:2012-01-31 15:56:43

标签: django django-admin inline

this question的一些评论表明,有更直接和最新的解决方案。 (我没找到)

(评论如下:“是的,它已经解决。只需在参数中传递相关实例,它就会预先填充它......”让我觉得现在很容易,但我仍然无法使其工作)

问题是我想向一些参与特定事件并由管理员选择的用户发送通知。 工作人员在事件的页面上,选择事件并保存它,而内联中的fk字段应该预先填充所涉及的用户名。 (等待工作人员检查或取消选中接收电子邮件通知)

出于可用性的原因,至关重要的是,这应该采用内联形式,因为已经有很多页面要从员工那里收集所有必要的信息。

有什么想法吗?

class When(models.Model):
    ...
    Date = models.DateTimeField(unique=True)
    Nameofevent = models.ForeignKey(Event, to_field='Name') #the Event model gets then username from models.ManyToManyField(User, through='Role')
    ...
    def getteam(self):
        teamlist = self.Nameofevent.Roleinevent.all() # gathering users which are involved
        return teamlist
    getteamresult = property(getteam)

class Notice(models.Model): #this will go to the inline
    ...
    Who = models.ForeignKey(User, blank=True)
    To_notice_or_not_to_notice = models.BooleanField(default=False)
    ...

class NoticeInline(admin.TabularInline):
    model = Notice
    extra = 9

class WhenAdmin(admin.ModelAdmin):
    list_display = ('Date', ...)
    readonly_fields = ('getteamresult', ) #this is to make clear i have access to what i want and i can display it. prints out as [<User: someone>, <User: someoneelse>]
    inlines = [NoticeInline] #but i want the User objects to prepopulate foreign field here in inline model
admin.site.register(When,WhenAdmin)

1 个答案:

答案 0 :(得分:1)

我不相信内联是这样的方式。如果应该提示工作人员向参与事件的用户发送电子邮件,并且需要控制实际通知哪些用户,那么您应该使用中间视图。

首先,您需要一个表单,让您选择属于​​该事件的用户。最初,我们将users字段设置为仅为所有用户,但在表单的__init__方法中,我们将采用“事件”kwarg并根据该字段过滤字段。

class UserNotifyForm(forms.Form):
    users = forms.ModelMultipleChoiceField(queryset=User.objects.all(), widget=forms.CheckboxSelectMultiple())

    def __init__(self, *args, **kwargs):
        event = kwargs.pop('event')
        super(UserNotifyForm, self).__init__(*args, **kwargs)
        if event:
            self.fields['users'].queryset = event.users.all()

其次,您在ModelAdmin上创建一个视图,其行为与普通表单视图一样:

def notify_users_view(self, request, object_id):
    event = get_object_or_404(Event, id=object_id)
    if len(request.POST):
        form = UserNotifyForm(request.POST, event=event)
        if form.is_valid():
            users = form.cleaned_data.get('users')
            # send email
            return HttpResponseRedirect(reverse('admin:yourapp_event_changelist'))
    else:
        form = UserNotifyForm(event=event)

    return render_to_response('path/to/form/template.html', {
        'event': event,
        'form': form,
    }, context_instance=RequestContext(request))

你当然需要为此创建模板,但这很简单。表单已经设置为显示一个复选框列表,每个用户一个,因此您可以获得所需的所有信息。

第三,将此观点与您的ModelAdmin网址绑定:

def get_urls(self):
    urls = super(MyModelAdmin, self).get_urls()

    info = (self.model._meta.app_label, self.model._meta.module_name)

    my_urls = patterns('',
        (r'^(?P<object_id>\d+)/notify/$', self.notify_users_view, name='%s_%s_notify' % info)
    )
    return my_urls + urls

第四,在保存后重写change_view以重定向到此视图:

def change_view(self, request, object_id, extra_context=None):
    response = super(MyModelAdmin, self).change_view(request, object_id, extra_context=extra_context)
    if len(request.POST):
        info = (self.model._meta.app_label, self.model._meta.module_name)
        response['Location'] = reverse('admin:%s_%s_notify', args=(object_id,))
    # Note: this will effectively negate the 'Save and Continue' and
    # 'Save and Add Another' buttons. You can conditionally check
    # for these based on the keys they add to request.POST and branch
    # accordingly to some other behavior you desire.
    return response