如何在管理中间页面中重用 Django 的管理外键小部件
How to reuse Django's admin foreign key widget in admin intermediate pages
我无法在 Django 的文档中的任何地方找到答案。不过,我并不感到惊讶,因为这个问题有点太复杂了,无法向搜索引擎提问。
我处于这样一种情况,我需要能够为 Django 管理站点上的模型的一个或多个条目重新分配 ForeignKey
字段。
到目前为止,我尝试使用自定义 action so that I can select the records I'm interested in and modify them all at once. But, then, I need to select the new related object I want their fk to be reassigned to. So, what I thought to do is an intermediate page 来实现,我会在其中显示我在管理页面周围看到的 fk 小部件:
但事实证明,这个小部件确实不是为公开使用而设计的。它没有记录并且使用起来非常复杂。到目前为止,我花了几个小时深入研究 Django 的代码,试图弄清楚如何使用它。
我觉得我正在尝试在这里做一些非常非常奇特的事情,所以,如果有其他解决方案,我都会听到。
重写 YourModelAdmin 的更改列表模板 class 除了添加按钮之外再添加一个按钮。
@admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
change_list_template = "custom_your_model_change_list.html"
在custom_your_model_change_list.html,
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a class="button" href="{% url 'your_reassign_url_name' %}">Reassign</a>
</li>
{{ block.super }}
{% endblock %}
将视图映射到 'your_reassign_url_name' 以处理您的请求。
在urls.py,
urlpatterns = [
path('reassign/', YourReassignView, name='your_reassign_url_name'),
]
forms.py,
class ReassignForm(forms.Form):
# your reassign field
reassign_field = forms.ModelChoiceField(queryset='your queryset')
# Select multiple objects
updatable_objects = forms.ModelMultipleChoiceField(queryset='All objects queryset',
widget=forms.CheckboxSelectMultiple)
在 views.py 中,根据 GET 请求,您呈现一个包含必填字段的表单,然后提交您的更新数据(重新分配值),然后您重定向管理 change_list 页面。
def YourReassignView(request):
if request.method == 'POST':
form = ReassignForm(request.POST)
if form.is_valid():
# you get value after form submission
reassign_field = form.cleaned_data.get('reassign_field')
updatable_objects = form.cleaned_data.get('updatable_objects')
# your update query
YourModel.objects.filter(
id__in=updatable_objects.values('id')
).update(field_name=reassign_field)
#after that you redirect admin change_list page with a success message
messages.success(request, 'Successfully reassign')
return redirect(reverse('admin:app_label_model_name_changelist'))
else:
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
else:
form = ReassignForm()
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
在reassign_template.html,
<form method="POST" action="{% url 'your_reassign_url_name' %}">
{% csrf_token %}
{{ form.as_p }}
<br>
<input type="submit" value="submit">
</form>
As shahbaz ahmad suggested, you can use ModelAdmin
's autocomplete_fields
创建一个 select
自动补全。
但是如果您受困于 Django 的外键小部件,例如,因为您的记录看起来相同并且在自动完成中无法区分,那么有一个解决方案。
事实证明 ModelAdmin
有一个 get_form
方法,您可以使用它来检索管理页面上使用的 ModelForm
。此方法接受一个 fields
kwargs,您可以使用它来 select 您想要在表单中检索的字段。像这样使用它:
class MyAdmin(ModelAdmin):
# define the admin subpath to your intermediate page
def get_urls(self):
return [
path(
"intermediate_page/",
self.admin_site.admin_view(self.intermediate_page),
name="intermediate_page",
),
*super().get_urls(),
]
def intermediate_page(self, request):
context = {
# The rest of the context from admin
**self.admin_site.each_context(request),
# Retrieve the admin form
"form": self.get_form(
request,
fields=[], # the fields you're interested in
)
}
return render(request, "admin/intermediate_page.html", context)
如果您的字段是只读的——这是我的情况——可编辑字段有一个解决方法:您可以覆盖 get_form
.
调用的 get_readonly_fields
方法
此方法接受一个 obj
参数,该参数通常采用正在编辑的对象的模型,或者在创建新条目时采用 None
。您可以劫持此参数以强制 get_readonly_fields
从只读字段中排除字段:
def get_readonly_fields(self, request, obj=None):
readony_fields = super().get_readonly_fields(request, obj)
if not (
isinstance(obj, dict)
and isinstance(obj.get("exclude_from_readonly_fields"), Collection)
):
return readony_fields
return set(readony_fields) - set(obj["exclude_from_readonly_fields"])
get_form
也有这个 obj
参数,它传递给 get_readonly_fields
所以你可以这样称呼它:
# the fields you're interested in
include_fields = []
self.get_form(
request,
obj={"exclude_from_readonly_fields": include_fields},
fields=include_fields
)
我无法在 Django 的文档中的任何地方找到答案。不过,我并不感到惊讶,因为这个问题有点太复杂了,无法向搜索引擎提问。
我处于这样一种情况,我需要能够为 Django 管理站点上的模型的一个或多个条目重新分配 ForeignKey
字段。
到目前为止,我尝试使用自定义 action so that I can select the records I'm interested in and modify them all at once. But, then, I need to select the new related object I want their fk to be reassigned to. So, what I thought to do is an intermediate page 来实现,我会在其中显示我在管理页面周围看到的 fk 小部件:
但事实证明,这个小部件确实不是为公开使用而设计的。它没有记录并且使用起来非常复杂。到目前为止,我花了几个小时深入研究 Django 的代码,试图弄清楚如何使用它。
我觉得我正在尝试在这里做一些非常非常奇特的事情,所以,如果有其他解决方案,我都会听到。
重写 YourModelAdmin 的更改列表模板 class 除了添加按钮之外再添加一个按钮。
@admin.register(YourModel)
class YourModelAdmin(admin.ModelAdmin):
change_list_template = "custom_your_model_change_list.html"
在custom_your_model_change_list.html,
{% extends "admin/change_list.html" %}
{% block object-tools-items %}
<li>
<a class="button" href="{% url 'your_reassign_url_name' %}">Reassign</a>
</li>
{{ block.super }}
{% endblock %}
将视图映射到 'your_reassign_url_name' 以处理您的请求。
在urls.py,
urlpatterns = [
path('reassign/', YourReassignView, name='your_reassign_url_name'),
]
forms.py,
class ReassignForm(forms.Form):
# your reassign field
reassign_field = forms.ModelChoiceField(queryset='your queryset')
# Select multiple objects
updatable_objects = forms.ModelMultipleChoiceField(queryset='All objects queryset',
widget=forms.CheckboxSelectMultiple)
在 views.py 中,根据 GET 请求,您呈现一个包含必填字段的表单,然后提交您的更新数据(重新分配值),然后您重定向管理 change_list 页面。
def YourReassignView(request):
if request.method == 'POST':
form = ReassignForm(request.POST)
if form.is_valid():
# you get value after form submission
reassign_field = form.cleaned_data.get('reassign_field')
updatable_objects = form.cleaned_data.get('updatable_objects')
# your update query
YourModel.objects.filter(
id__in=updatable_objects.values('id')
).update(field_name=reassign_field)
#after that you redirect admin change_list page with a success message
messages.success(request, 'Successfully reassign')
return redirect(reverse('admin:app_label_model_name_changelist'))
else:
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
else:
form = ReassignForm()
context_data = {'form': form}
return render(request, 'reassign_template.html', context=context_data)
在reassign_template.html,
<form method="POST" action="{% url 'your_reassign_url_name' %}">
{% csrf_token %}
{{ form.as_p }}
<br>
<input type="submit" value="submit">
</form>
As shahbaz ahmad suggested, you can use ModelAdmin
's autocomplete_fields
创建一个 select
自动补全。
但是如果您受困于 Django 的外键小部件,例如,因为您的记录看起来相同并且在自动完成中无法区分,那么有一个解决方案。
事实证明 ModelAdmin
有一个 get_form
方法,您可以使用它来检索管理页面上使用的 ModelForm
。此方法接受一个 fields
kwargs,您可以使用它来 select 您想要在表单中检索的字段。像这样使用它:
class MyAdmin(ModelAdmin):
# define the admin subpath to your intermediate page
def get_urls(self):
return [
path(
"intermediate_page/",
self.admin_site.admin_view(self.intermediate_page),
name="intermediate_page",
),
*super().get_urls(),
]
def intermediate_page(self, request):
context = {
# The rest of the context from admin
**self.admin_site.each_context(request),
# Retrieve the admin form
"form": self.get_form(
request,
fields=[], # the fields you're interested in
)
}
return render(request, "admin/intermediate_page.html", context)
如果您的字段是只读的——这是我的情况——可编辑字段有一个解决方法:您可以覆盖 get_form
.
get_readonly_fields
方法
此方法接受一个 obj
参数,该参数通常采用正在编辑的对象的模型,或者在创建新条目时采用 None
。您可以劫持此参数以强制 get_readonly_fields
从只读字段中排除字段:
def get_readonly_fields(self, request, obj=None):
readony_fields = super().get_readonly_fields(request, obj)
if not (
isinstance(obj, dict)
and isinstance(obj.get("exclude_from_readonly_fields"), Collection)
):
return readony_fields
return set(readony_fields) - set(obj["exclude_from_readonly_fields"])
get_form
也有这个 obj
参数,它传递给 get_readonly_fields
所以你可以这样称呼它:
# the fields you're interested in
include_fields = []
self.get_form(
request,
obj={"exclude_from_readonly_fields": include_fields},
fields=include_fields
)