如何在Django管理页面的详细信息页中添加下载文件链接?

6

我想创建一个包含字符串的文件,并允许用户在管理员详细页面上单击按钮时下载该文件。 有什么建议吗?

可能要向表单添加html?但是我怎么做呢?我是Django的新手。


“在管理员表单中”是什么意思?是在管理员列表页面还是在管理员详细页面? - Ykh
管理员详细页面,可以编辑模型字段并保存更改。 - Michael
请参见 https://docs.djangoproject.com/en/2.0/ref/contrib/admin/actions/。 - Selcuk
@Selcuk 我需要在管理员详细页面中添加按钮,而不是列表页面。 - Michael
3个回答

21

您可以按照以下方式进行:

class YourAdmin(ModelAdmin):   
     # add the link to the various fields attributes (fieldsets if necessary)
    readonly_fields = ('download_link',)
    fields = (..., 'download_link', ...)

    # add custom view to urls
    def get_urls(self):
        urls = super(YourAdmin, self).get_urls()
        urls += [
            url(r'^download-file/(?P<pk>\d+)$', self.download_file, 
                name='applabel_modelname_download-file'),
        ]
        return urls

    # custom "field" that returns a link to the custom function
    def download_link(self, obj):
        return format_html(
            '<a href="{}">Download file</a>',
            reverse('admin:applabel_modelname_download-file', args=[obj.pk])
        )
    download_link.short_description = "Download file"

    # add custom view function that downloads the file
    def download_file(self, request, pk):
        response = HttpResponse(content_type='application/force-download')
        response['Content-Disposition'] = 'attachment; filename="whatever.txt"')
        # generate dynamic file content using object pk
        response.write('whatever content')
        return response

你能详细说明一下 admin:applabel_modelname_download-file 是做什么的吗? - Michael
这是视图的名称,如 reverseurl 模板标签所使用。常见的管理视图遵循 applabel_modelname_viewname 模式。这就是为什么我使用它的原因。 - user2390182
3
我遇到了“无法找到反向匹配”的错误。我使用了 Model.__meta.app_label 的值作为 applabel,并将实际模型名称转换为小写字母作为 modelname。就像你一样,我写了 download-file。我有什么遗漏吗? - Michael
1
我也做了同样的事情,但现在当我尝试添加一个对象时,我得到了“没有反向匹配”的错误。 - foragerDev
对于所有添加新对象时出现“不可逆数学”错误的用户:在download_link函数中,加入if obj.id is not None then return link。否则返回“ - ”。 - Yosef SL
1
重要提示,像这样添加的 URL/路径是不安全的!您必须手动添加@staff_member_required 装饰器(例如)才能防止匿名和非工作人员请求访问。如果您什么都不做,那么该文件下载将对 所有人 开放。 - fechnert

4
在你的应用程序的models.py字段中,添加以下代码片段。
from django.utils.safestring import mark_safe

def fieldname_download(self):
    return mark_safe('<a href="/media/{0}" download>{1}</a>'.format(
        self.fieldname, self.fieldname))

fieldname_download.short_description = 'Download Fieldname'

然后在你的 admin.py 中,为该模型添加此字段到 readonly_fields 中。
readonly_fields = ('fieldname_download', )

在您的settings.py文件中,您需要指定一个根路径,用于存放文件,并指定一个基本URL来访问它们:
MEDIA_ROOT=(str, 'path/to/your/media/directory/'),
MEDIA_URL=(str,'/media/'),

看起来我还需要将 fieldname_download 添加到 fields 属性中,你知道为什么吗? - Michael
1
如果您在该模型的自定义管理中定义了“fields”选项,则需要将“fieldname_download”添加到其中。https://docs.djangoproject.com/en/2.0/ref/contrib/admin/#django.contrib.admin.ModelAdmin.fields - Mehak

1

关于将下载链接作为新字段添加到详情页,有两种答案,比在AdminFileWidget中添加下载链接更容易。我写下这个答案是为了帮助需要在AdminFileWidget中添加下载链接的人。 最终结果如下:

enter image description here

实现此目标的方法是:
1. models.py
class Attachment(models.Model):
    name = models.CharField(max_length=100,
                            verbose_name='name')
    file = models.FileField(upload_to=attachment_file,
                            null=True,
                            verbose_name='file ')

2 views.py:

class AttachmentView(BaseContextMixin, DetailView):
    queryset = Attachment.objects.all()
    slug_field = 'id'

    def get(self, request, *args, **kwargs):
        instance = self.get_object()
        if settings.DEBUG:
            response = HttpResponse(instance.file, content_type='application/force-download')
        else:
            # x-sendfile is a module of apache,you can replace it with something else
            response = HttpResponse(content_type='application/force-download')
            response['X-Sendfile'] = instance.file.path
        response['Content-Disposition'] = 'attachment; filename={}'.format(urlquote(instance.filename))
        return response

3 urls.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path('attachment/<int:pk>/', AttachmentView.as_view(), name='attachment'),
]

4 admin.py

from django.urls import reverse
from django.contrib import admin
from django.utils.html import format_html
from django.contrib.admin import widgets


class DownloadFileWidget(widgets.AdminFileWidget):
    id = None
    template_name = 'widgets/download_file_input.html'

    def __init__(self, id, attrs=None):
        self.id = id
        super().__init__(attrs)

    def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        print(self, name, value, attrs, self.id)
        context['download_url'] = reverse('attachment', kwargs={'pk': self.id})
        return context

class AttachmentAdmin(admin.ModelAdmin):
    list_display = ['id', 'name', '_get_download_url']
    search_fields = ('name',)
    my_id_for_formfield = None

    def get_form(self, request, obj=None, **kwargs):
        if obj:
            self.my_id_for_formfield = obj.id
        return super(AttachmentAdmin, self).get_form(request, obj=obj, **kwargs)

    def formfield_for_dbfield(self, db_field, **kwargs):
        if self.my_id_for_formfield:
            if db_field.name == 'file':
                kwargs['widget'] = DownloadFileWidget(id=self.my_id_for_formfield)

        return super(AttachmentAdmin, self).formfield_for_dbfield(db_field, **kwargs)

    def _get_download_url(self, instance):
        return format_html('<a href="{}">{}</a>', reverse('attachment', kwargs={'pk': instance.id}), instance.filename)

    _get_download_url.short_description = 'download'

admin.site.register(Attachment, AttachmentAdmin)

5 download_file_input.html

{% include "admin/widgets/clearable_file_input.html" %}
<a href="{{ download_url }}">Download {{ widget.value }}</a>

就这些!


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接