在Django Admin中,当你修改一个对象属性时,如果有一个ForeignKey,它将有一个包含所有选项的下拉框,另外还有一个“+”按钮来添加更多选项。当我点击它时,我想预先填写一些数据。
我注意到如果我可以修改网址,我可以这样做(例如:http://localhost:8000/admin/app/model/add/?field=value
其中field
和value
是以编程方式修改的。)
我想我必须覆盖forms.ModelForm
使用的admin.ModelAdmin
中的内容,但我不确定是什么。
答案 0 :(得分:7)
Django允许您替换请求的GET dict(用于预填充管理表单)。
如果要在url中发送模型表单的字段值,Django将自动从url get参数中获取值。例如,考虑
"http://myhost/admin/app/model/add/?name=testname"
,它会在管理添加视图模板中预填充表单的'name'字段,其值为'testname'。
但是如果你要在你的网址中发送任何id,那么你需要通过覆盖add_view函数来修改get参数。
取自stackoverflow回答
class ArticleAdmin(admin.ModelAdmin):
// ...
def add_view(self, request, form_url='', extra_context=None):
source_id = request.GET.get('source',None)
if source_id != None:
source = FeedPost.objects.get(id=source_id)
// any extra processing can go here...
g = request.GET.copy()
g.update({
'title':source.title,
'contents':source.description + u"... \n\n[" + source.url + "]",
})
request.GET = g
return super(ArticleAdmin, self).add_view(request, form_url, extra_context)
这只是一个例子。用你的模型和字段来表达:)
答案 1 :(得分:1)
这很老,但这可能是一个难题,被接受的答案并没有解决我的真正问题。
据我所知,问题是如何在相关模型的管理表单上预填充数据,该表单在您单击小部件旁边的绿色+
(“添加其他”)后弹出。 ForeignKey
或ManyToManyField
。如下所示。
根据(currently) accepted answer和here的建议,可以通过为要预填充的每个字段添加URL参数来实现。图片中也对此进行了说明。
但是,尚未解决的问题是:
究竟如何将这些URL参数添加到+
(“添加其他”)链接使用的URL中?
假设预填充值取决于客户端上表单的当前状态,例如上图中选择的organization
,我想使用JavaScript是合理的。
注意:如果预填充的值不依赖于表单状态也不依赖于当前请求,则other options可能更合适。
以下是用于创建上面图像的最小示例:
models.py :(为了方便起见,我们也将管理内容放在这里)
from django.db import models
from django.contrib import admin
class Organization(models.Model):
pass
class Participant(models.Model):
organization = models.ForeignKey(to=Organization, on_delete=models.CASCADE)
class Event(models.Model):
organization = models.ForeignKey(to=Organization, on_delete=models.CASCADE)
participants = models.ManyToManyField(to=Participant)
admin.site.register(Organization)
admin.site.register(Participant)
admin.site.register(Event)
现在,我们为change_form
模型扩展了Django admin Event
模板(基于docs),并添加了一些JavaScript。
按照these的说明设置模板文件夹,不要忘记将模板文件夹添加到DIRS
中的settings.TEMPLATES
。
templates / admin / my_app / event / change_form.html :
{% extends 'admin/change_form.html' %}
{% load static %}
{% block admin_change_form_document_ready %}
{{ block.super }}
<script type="text/javascript">
var add_participant_url = document.getElementById("add_id_participants").href;
var organization = document.getElementById("id_organization")
organization.onchange = function() {
document.getElementById("add_id_participants").href =
add_participant_url + "&organization=" + organization.value;
};
</script>
{% endblock %}
免责声明:我对JavaScript的了解非常有限,因此欢迎提出任何改进建议。
基本上,每当用户在“更改事件”表单上更改所选组织时,“添加其他参与者”(+)链接的URL都会更新为所选值,因此“添加参与者”上的组织字段表格将被预先填写。
答案 2 :(得分:1)
Aks 的回答对我不起作用,因为我在 source
字典中找不到 request.GET
字段。但是,我能够通过将以下函数添加到我的 TestAdmin
类来使其工作 - 这是单击添加按钮时打开的管理员
def get_form(self, request, obj=None, **kwargs):
form = super(TestAdmin, self).get_form(request, obj, **kwargs)
// using URL pattern to get the refrrer object id
admin_referer = re.match(".*(/adminame/)(.+)(/change/)",
request.META.get('HTTP_REFERER'))
if request.GET.get('_popup', None) and admin_referer:
test_model = TestModel.objects.get(id=admin_referer[2])
form.base_fields['prepopulate_field'].initial = test_model
form.base_fields['prepopulate_field'].widget.can_add_related = False
form.base_fields['prepopulate_field'].widget.can_change_related = False
form.base_fields['prepopulate_field'].widget.can_delete_related = False
return form
我希望有一种更好的方式来获取引用者的对象 ID,但我只能通过这种方式来工作。
答案 3 :(得分:0)
这是我的other answer的替代方法。
此替代方法不使用JavaScript,因此只能在服务器端使用,这有很多缺点。不过,难题的某些部分可能对某人有用。
基本思想是:
“添加其他”(+)按钮提供了到相关模型管理员视图的链接。
此链接的URL在related_widget_wrapper.html模板中构建。
此模板使用url_params
变量,该变量是通过RelatedFieldWidgetWrapper.get_context()方法创建的。
要预先填写相关表格,我们需要在小部件的上下文中将自定义URL参数附加到此url_params
变量中,以便我们可以利用默认的小部件模板。
据我所知,最简单的方法是为RelatedFieldWidgetWrapper.get_context()
方法创建包装。
然后,我们扩展ModelAdmin.formfield_for_dbfield()
方法,并在此处使用我们的自定义包装,以便它可以访问当前请求。
据我所知,ModelAdmin docs ...中没有提到formfield_for_dbfield
方法,但这是where Django applies the RelatedFieldWidgetWrapper。
这是一个最小的例子:
from django.db import models
from django.contrib import admin
class Organization(models.Model):
pass
class Participant(models.Model):
organization = models.ForeignKey(to=Organization, on_delete=models.CASCADE)
class Event(models.Model):
organization = models.ForeignKey(to=Organization, on_delete=models.CASCADE)
participants = models.ManyToManyField(to=Participant)
def wrap_extra_url_params(original_get_context, extra_url_params: str):
def get_context_with_extra_url_params(*args, **kwargs):
context = original_get_context(*args, **kwargs)
if 'url_params' in context:
context['url_params'] += '&{}'.format(extra_url_params)
return context
return get_context_with_extra_url_params
class EventAdmin(admin.ModelAdmin):
def formfield_for_dbfield(self, db_field, request, **kwargs):
formfield = super().formfield_for_dbfield(db_field, request, **kwargs)
if db_field.name == 'participants':
# get event organization from current request
event_id = request.resolver_match.kwargs.get('object_id', None)
if event_id:
event = Event.objects.get(id=event_id)
# override the "add another participant" link
formfield.widget.get_context = wrap_extra_url_params(
original_get_context=formfield.widget.get_context,
extra_url_params='organization={}'.format(
event.organization_id))
return formfield
admin.site.register(Organization)
admin.site.register(Participant)
admin.site.register(Event, EventAdmin)
请注意,服务器端无法知道用户是否更改了所选组织,因此仅在修改现有Event
实例时有效。
在某种程度上这是可行的,但我会选择JavaScript选项。