定制django admin页面的跳转

本文详细介绍了如何在Django Admin的change_view、add_view和delete_view页面实现自定义跳转逻辑,通过重写父类方法,将操作后的结果导向指定URL,解析了服务器响应与浏览器跳转机制,特别指出在表单提交前页面未发生跳转的现象。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在django admin的 change_view,  add_view和delete_view页面,如果想让页面完成操作后跳转到我们想去的url,该怎么做

默认django admin会跳转到changelist_view页面

 

------------------------------

下面的代码是django1.6的

 

下面是一个可行的做法,写admin model的时候重写父类admin.ModelAdmin的change_view 方法

from django.contrib import admin
class MyAdmin(admin.ModelAdmin):
    def change_view(self, request, object_id, form_url='', extra_context=None):
        result_template = super(MyAdmin, self).change_view(request, object_id, form_url, extra_context)
        result_template['location'] = '/dest/url'
        return result_template           

 

可以看到,就是调用ModelAdmin的change_view得到结果,然后给 result_template做了一个这个操作

 result_template['location'] = '/dest/url'

然后返回

 

为什么这样可行? 我们看看发生了什么

 

我们重写change_view,当然参数必须和父类一样了

 

首先调用了父类ModelAdmin.change_view的这个函数,这个函数返回了什么呢

追溯一下源代码,它返回的是一个TemplateResponse对象, 是通过调用 ModelAdmin.render_change_form()

 

return TemplateResponse(request, form_template or [
    "admin/%s/%s/change_form.html" % (app_label, opts.model_name), 
    "admin/%s/change_form.html" % app_label,
    "admin/change_form.html"        
 ], context, current_app=self.admin_site.name)     

 

那么接下来我们看看TemplateResponse

 其实有这样的派生关系: TemplateResponse <----  SimpleTemplage  <-----HttpRespone  <--- HttpResponseBase

 

HttpResponse里实现了 

def __setitem__(self, header, value):
    header = self._convert_to_charset(header, 'ascii')
    value = self._convert_to_charset(value, 'latin-1', mime_encode=True)
    self._headers[header.lower()] = (header, value)

 

而如果一个类实现了 __setitem__,  那么[] 操作符就会去调用这个函数(相当于C++中的重载)

result_template['location'] = '/dest/url'

 

所以上面这行代码就在服务器返回的Response的header中写入了location, 而浏览器收到的Http Response的header中如果有location,就会跳转到location指定的 url

 

-------------------------------------------------------

 

但是我还发现了一个现象, 当进入到一个model item的change_view界面时,也就是GET请求这个url,虽然服务端返回了location,但是浏览器没有跳转,可能是因为当前有form需要提交.

 而在change_view界面修改完后, 点击提交表单,浏览器收到服务端的location后,就发生了跳转.

 

 

 

 

 

 

 

 

 

 

转载于:https://www.cnblogs.com/livingintruth/p/3738601.html

<think>我们有一个需求:在DjangoAdmin中实现一对多关系的跳转。例如,一个主模型(如Order)有多个子模型(如OrderItem),我们希望在Order的Admin面中能够跳转到相关的OrderItem列表面。实现思路:1.在Admin中,我们可以通过自定义列表显示(list_display)来添加一个链接字段。2.使用一个自定义的方法来生成链接,该方法返回一个HTML的`<a>`标签,指向关联对象的列表面(通常是通过过滤条件显示与该主对象相关的所有子对象)。3.在DjangoAdmin中,子对象的列表面URL通常类似于:`/admin/app_name/model_name/?foreignkey_field_id=object_id`步骤:假设有两个模型:`Order`(主模型)和`OrderItem`(子模型,通过外键关联到Order)。1.定义模型(models.py):```pythonfromdjango.dbimportmodelsclassOrder(models.Model):#字段...customer=models.CharField(max_length=100)def__str__(self):returnf"Order{self.id}"classOrderItem(models.Model):order=models.ForeignKey(Order,on_delete=models.CASCADE)product=models.CharField(max_length=100)quantity=models.IntegerField()def__str__(self):returnf"OrderItem{self.id}"```2.在Admin中注册Order模型,并自定义一个链接字段,用于跳转到该订单对应的所有OrderItem的列表面。```pythonfromdjango.contribimportadminfromdjango.urlsimportreversefromdjango.utils.htmlimportformat_htmlfrom.modelsimportOrder,OrderItem#先注册OrderItem,这样在OrderAdmin中我们可以使用OrderItem的admin信息@admin.register(OrderItem)classOrderItemAdmin(admin.ModelAdmin):list_display=('id','order','product','quantity')list_filter=('order',)@admin.register(Order)classOrderAdmin(admin.ModelAdmin):list_display=('id','customer','view_items_link')defview_items_link(self,obj):#生成一个跳转到OrderItem列表的链接,并过滤出当前Order的OrderItem#这里假设OrderItem的admin注册的app是当前app,模型名为'orderitem'url=reverse('admin:app_orderitem_changelist')#注意:app是实际app的名字,例如'myapp'#构建带过滤参数的URL:/admin/myapp/orderitem/?order__id=obj.idurl_with_filter=f"{url}?order__id={obj.id}"returnformat_html('<ahref="{}">ViewItems</a>',url_with_filter)view_items_link.short_description='Items'#设置表头显示```注意:-`reverse('admin:app_orderitem_changelist')`中的`app`需要替换为你的应用名(即包含OrderItem模型的应用的名字),`orderitem`是模型名的全小写形式。-在URL过滤参数中,我们使用`order__id`是因为在OrderItem模型中,外键字段名为`order`,我们想通过其id进行过滤。在Django中,外键字段在数据库中的列名会加上`_id`后缀,但这里在Admin的URL过滤中,我们使用的是字段名加双下划线加字段(即外键关联到Order的主键,默认是id)的方式。所以这里写成`order__id`。但是,上面的方法有一个问题:在Admin中,过滤条件通常使用外键字段名(不带_id)加上`__id`(或者直接使用外键字段名,后面跟主键值)?实际上,我们也可以直接使用`order`(外键字段)来过滤,因为`order`是一个外键,它指向Order的主键(id),所以我们可以用`order=obj.id`。因此,我们可以修改为:```pythondefview_items_link(self,obj):url=reverse('admin:myapp_orderitem_changelist')#替换myapp为你的应用名url_with_filter=f"{url}?order={obj.id}"returnformat_html('<ahref="{}">ViewItems</a>',url_with_filter)```这是因为在OrderItemAdmin中,我们通常会在`list_filter`中包含`order`,这样在URL中就可以用`order=1`这样的形式来过滤。另外,我们也可以使用更通用的方式,通过获取OrderItem模型的`_meta`信息来构建应用名和模型名,但通常直接写死应用名和模型名更简单。3.如果希望跳转到一个更友好的面(例如自定义面,而不是Admin的默认列表),那么可以自定义一个视图,然后在Admin中添加一个链接指向该视图。但是,根据要求,我们只使用Admin自带的列表面,所以上述方法即可。示例中,我们在Order的列表面添加了一个“ViewItems”的链接,点击后跳转到该订单的OrderItem列表。注意:上面的代码中,`view_items_link`方法返回的是HTML字符串,所以使用`format_html`来标记安全,避免转义。另外,如果一对多关系是通过另一个模型的外键关联,或者多对多关系,方法类似。引用说明:-在DjangoAdmin中,我们经常需要自定义Admin的显示和链接,这可以通过在Admin类中定义方法并添加到`list_display`中实现[^1][^3]。-使用`reverse`函数生成Admin的URL是常见的做法[^4]。因此,我们按照上述步骤实现即可。但是,请注意:上面的示例中,我们假设应用名为`myapp`,实际使用中请替换为你的应用名。如果应用名有多个单词,需要保持一致。如果模型在多个应用中,那么需要指定正确的应用命名空间。Admin的URL命名空间是`admin`,具体格式为:`admin:<app_label>_<model_name>_changelist`。另外,如果希望链接在新标签打开,可以在`<a>`标签中添加`target="_blank"`属性。修改链接方法如下:```pythonreturnformat_html('<ahref="{}"target="_blank">ViewItems</a>',url_with_filter)```根据需求选择。最后,我们给出一个完整的示例代码(假设应用名为`orders`):models.py:```pythonfromdjango.dbimportmodelsclassOrder(models.Model):customer=models.CharField(max_length=100)#其他字段...def__str__(self):returnf"Order{self.id}"classOrderItem(models.Model):order=models.ForeignKey(Order,on_delete=models.CASCADE,related_name='items')product=models.CharField(max_length=100)quantity=models.IntegerField()#其他字段...def__str__(self):returnf"OrderItem{self.id}"```admin.py:```pythonfromdjango.contribimportadminfromdjango.urlsimportreversefromdjango.utils.htmlimportformat_htmlfrom.modelsimportOrder,OrderItem@admin.register(OrderItem)classOrderItemAdmin(admin.ModelAdmin):list_display=('id','order','product','quantity')list_filter=('order',)@admin.register(Order)classOrderAdmin(admin.ModelAdmin):list_display=('id','customer','view_items')defview_items(self,obj):#构建跳转到OrderItem列表的URL,并过滤当前订单url=reverse('admin:orders_orderitem_changelist')url_with_filter=f"{url}?order={obj.id}"returnformat_html('<ahref="{}">查看订单项</a>',url_with_filter)view_items.short_description='订单项'```这样,在Order的Admin列表面,每一行都会有一个“查看订单项”的链接,点击后跳转到该订单对应的OrderItem列表。注意:如果外键字段在OrderItem中不叫`order`,请相应地修改过滤参数(例如,如果字段名为`order_fk`,那么过滤参数应为`order_fk=obj.id`)。另外,如果外键字段允许为空,那么过滤条件中可能还需要考虑空值的情况,但这里我们的外键不允许为空(因为`on_delete=models.CASCADE`没有设置`null=True`)。这样,我们就实现了在Admin中一对多关系的跳转。相关问题:1.如何在DjangoAdmin中实现多对多关系的跳转?2.如何在DjangoAdmin的详情面添加自定义的链接?3.如何自定义DjangoAdmin的列表面,使其显示关联对象的数量统计?</think>### Django Admin 实现一对多关系跳转Django Admin 中实现一对多关系的跳转,可以通过自定义 `ModelAdmin` 类和模板实现。以下是一个完整示例,演示如何在父对象详情添加跳转到相关子对象列表的链接: #### 模型示例 (models.py) ```python from django.db import models # 父模型 class Author(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name # 子模型(一对多关系) class Book(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books') title = models.CharField(max_length=200) publish_date = models.DateField() def __str__(self): return self.title ``` #### Admin 配置 (admin.py) ```python from django.contrib import admin from django.urls import reverse from django.utils.html import format_html from .models import Author, Book @admin.register(Book) class BookAdmin(admin.ModelAdmin): list_display = ('title', 'author', 'publish_date') list_filter = ('author',) @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): list_display = ('name', 'view_books_link') # 自定义方法生成跳转链接 def view_books_link(self, obj): count = obj.books.count() url = ( reverse('admin:myapp_book_changelist') # 替换myapp为你的应用名 + f'?author__id__exact={obj.id}' ) return format_html('<a href="{}">查看相关书籍 ({}本)</a>', url, count) view_books_link.short_description = '相关书籍' # 列标题 view_books_link.allow_tags = True # 允许HTML标签 ``` #### 关键实现说明: 1. **自定义链接方法**: - 使用 `reverse()` 生成子模型列表 URL - 添加过滤参数 `?author__id__exact={obj.id}` 筛选关联对象 - `format_html()` 安全渲染 HTML 链接 2. **URL 格式说明**: - `admin:app_model_changelist` 是 Admin 列表的标准 URL 名称 - 过滤参数语法:`字段名__查询条件=值` 3. **效果**: - 作者列表显示"查看相关书籍"链接 - 点击跳转到书籍列表(自动过滤该作者的书籍) - 显示关联书籍数量统计 #### 高级用法:在详情添加跳转 ```python # 在AuthorAdmin中添加 fieldsets = ( (None, { 'fields': ('name', 'books_section') }), ) def books_section(self, obj): books = obj.books.all() links = [] for book in books: url = reverse('admin:myapp_book_change', args=[book.id]) links.append(f'<li><a href="{url}">{book.title}</a></li>') return format_html('<ul>{}</ul>', ''.join(links)) if links else "无相关书籍" books_section.short_description = '著作列表' books_section.allow_tags = True ``` #### 注意事项: 1. 替换 `myapp` 为你的实际应用名 2. 外键字段名需与实际模型字段匹配 3. 使用 `allow_tags=True` 允许渲染 HTML 4. 复杂场景可结合 Django 的 `@admin.display` 装饰器 这种方法无需修改核心代码,通过标准 Admin 扩展机制实现,保持了 Django Admin 的稳定性[^1][^3]。 --- ### 相关问题 1. 如何在 Django Admin 中实现多对多关系的跳转? 2. 如何自定义 Django Admin 的筛选器实现更复杂的关系过滤? 3. 怎样在 Django Admin 详情内联显示关联对象并添加操作按钮? 4. 如何优化 Django Admin 处理大型数据集时的关联对象加载性能?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值