Django admin 创建或编辑模型实例时出现404错误。

5

我目前正在调试一个Django网站的奇怪问题,当在管理员界面中创建新实例或编辑现有实例时,特定模型会触发404错误。

具体来说,错误发生在提交表单时。 我可以很好地GET更改表单。

这仅在现场网站上发生,并且仅在保存此模型时才会发生。 所有其他模型都按预期工作,当我在本地运行它时,一切正常。 当以编程方式创建时,在现场和本地都一切正常。

这是我的模型:

class Content(models.Model):
    """Base Content class."""
    title = models.CharField(max_length=200)
    body = RichTextUploadingField(max_length=30000, blank=True, null=True, config_name='admin')
    date_created = models.DateTimeField(auto_now_add=True)
    date_updated = models.DateTimeField(auto_now=True)
    author = models.ForeignKey(to=User, on_delete=models.CASCADE)
    slug = models.SlugField(max_length=100, null=True, default=None)

    class Meta:
        abstract = True


class ContentPage(Content):
    """Represents a page of generic text content."""
    title = models.CharField(max_length=200, unique=True)
    has_url = models.BooleanField(default=False, help_text='Sets the page to accessible via a URL.')
    banner = models.ImageField(upload_to='myfiles/banners/', blank=True, null=True)

    def save(self, *args, **kwargs):
        """Create the slug from the title."""
        self.slug = slugify(self.title[:100])
        super(ContentPage, self).save(*args, **kwargs)

ContentPage类是在管理界面中触发问题的那个类。我从Content继承的另一个类运行正常。

我已经将我的管理设置简化为以下内容,问题仍然存在:

class CustomAdminSite(AdminSite):

    def get_urls(self):
        """Define custom admin URLs."""
        urls = super(CustomAdminSite, self).get_urls()

        # Append some new views here...

        return urls


admin_site = CustomAdminSite()
admin_site.register(ContentPage)

以下是一个基本的URL配置,可以重现问题:

from myapp.admin import admin_site


urlpatterns = [
    path('mycooladminsite/', admin_site.urls),
    path('', include('myapp.urls')),
]

我检查了以下几点:

  • 没有信号与保存冲突。
  • 在模型的save()方法中没有执行任何操作。
  • 已检查get_object_or_404()调用,未发现会影响此问题的内容。

我花费了几个小时的时间研究这个问题,但目前还没有头绪。

数据库引擎使用的是mysql.connector.django,Django版本为2.2.11。但由于该网站,我无法更改引擎或升级至3.x版本。

这是一个最近才出现的问题,以前从未注意到。

更新:

我将问题缩小到了ImageField。当我将其从显示字段中移除时,保存就不会出现问题了。

我正在使用一个自定义管理表单,但这似乎不是问题所在。我尝试使用默认选项,但仍然出现问题。我一直在寻找存储类中的异常,但没有找到。我将其全部剥离,但错误仍然存在。

我在Firefox开发人员工具中检查了本地302 POST和生产404 POST,它们几乎相同,但生产服务器是Apache且x-powered-by为Phusion Passenger,而本地服务器是WSGIServer/0.2 CPython/3.7.3。

我实际上注意到在生产环境中会出现其他multipart/form-data表单的404错误,而之前从未遇到过。


我后来找到了一个类似的问题,但由于没有被接受的答案,所以无法标记为重复。不过我已经在这个问题上添加了赏金:https://dev59.com/JlMH5IYBdhLWcg3w_Wd_ 其他信息请参见此处:https://forum.djangoproject.com/t/django-contrib-admin-options-add-view-404-with-image-field/3246 - alstr
2个回答

0

我不知道问题出在哪里,但如果你没有日志,可以在生产环境下运行 DEBUG=False,不要使用 Sentry 等工具。如果只有在保存 ContentPage 管理表单时才会出现问题,那么你可以在你的 ModelAdmin 中覆盖 save_newsave_model 方法。


    def save_model(self, request, obj, form, change):
        try:
            obj.save()
        except Exception:
            logging.exception("blah")

(或相应地使用save_new

并检查日志。

或者直接使用Sentry。他们有免费的层次。


我刚刚尝试了这个,但在save_model中没有发生任何异常。 我确实在我的handler404视图中记录了异常,并且它说找不到匹配的路径...但我不知道原因。 它可以在GET请求上正常加载页面,路径当然是相同的。 - alstr
你说它发生在表单保存时。尝试覆盖save_new - Tom Wojcik
明天会尝试解决。问题可能发生在对象保存之前 - 似乎是ImageField/storage类在抱怨某些东西,但目前还不确定。我会进一步查看。 - alstr
我应该在哪里覆盖 save_new?我只知道这是一个 ModelFormSet 函数。此外,问题发生在编辑现有实例时,而不仅仅是创建新实例时。我已经将问题缩小到了 ImageField,并在问题中添加了更多信息。 - alstr
是的,那么我认为它不适用于您的情况。据我所知,它只在那里。那么也许是模型上的“保存”方法? - Tom Wojcik
模型的 save 方法没有引发任何异常。我能够获取到任何异常细节的唯一位置是我的 handler404 函数,但它并没有告诉我它来自代码的哪个部分。它只告诉我当前的 URL 是 404,但实际上不是这样,因为我在地址栏中按下回车键后它可以正常加载。我认为背景中的某个其他 URL 可能会出现 404 错误... - alstr

0

我从未完全弄清楚发生了什么,但我设计了一个解决方法,至少允许表单正确地运行,正如我在回答相关问题中所讨论的:

  1. 编辑django.core.handlers.base中的_get_response(self, request)。将resolver_match = resolver.resolve(request.path_info)更改为resolver_match = resolver.resolve(request.path)

  2. 添加此中间件(根据您的实际需求进行调整):

     class ImageField404Middleware:
         def __init__(self, get_response):
             self.get_response = get_response
    
         def __call__(self, request):
             response = self.get_response(request)
    
             if (request.method == 'POST' and request.user.is_superuser and response.status_code == 302
                     and request.get_full_path().startswith('/pathtoadmin/')):
                 post_messages = get_messages(request)
                 for message in post_messages:
                     if ('was added successfully' in message.message or 'was changed successfully' in message.message
                             and message.level == message_levels.SUCCESS):
                         messages.success(request, message.message)
                         redirect_url = request.get_full_path()
                         if '_addanother' in request.POST:
                             redirect_url = re.sub(r'[^/]*/[^/]*/$', 'add/', redirect_url)
                         elif '_save' in request.POST:
                             redirect_url = re.sub(r'[^/]*/[^/]*/(\?.*)?$', '', redirect_url) 
                             if '_changelist_filters' in request.GET:
                                 preserved_filters = parse.parse_qsl(request.GET['_changelist_filters'])
                                 redirect_url += '?' + parse.urlencode(preserved_filters)   
                         elif '_continue' in request.POST:
                             redirect_url_search = re.search(r'((?<=href=)[^>]*)', message.message)    
                             if redirect_url_search:                                                  
                                 redirect_url = redirect_url_search.group(0)                                                  
                                 redirect_url = re.sub(r'[\\"]*', '', redirect_url).replace('/pathtoadmin/pathtoadmin/', '/pathtoadmin/')
                         return HttpResponseRedirect(redirect_url)
    
             return response
    

虽然不是最理想的解决方案,但目前对我来说可行。

您可以在此处阅读更多详细信息: https://medium.com/@mnydigital/how-to-resolve-django-admin-404-post-error-966ce0dcd39d

我相信这些问题至少部分原因是由于ModSecurity Apache引起的。主机提供商重新配置后,我遇到的一些问题消失了。


你可以使用“猴子补丁”来安装此修复程序,并确保未来的升级安全。 - user1600649

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