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

Django admin 404 error when creating or editing a model instance

我目前正在调试 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 class 是在管理界面中触发问题的原因。我从 Content 继承的另一个 class 工作正常。

我已将我的管理设置剥离为以下内容,但它仍在发生:

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')),
]

我检查过的其他一些内容:

我已经花了几个小时来研究这个问题,但我目前正处于困境。

数据库引擎为mysql.connector.django,Django 版本为 2.2.11。我无法为该站点更改引擎或将 Django 更新为 3.x。

这是我以前没有注意到的最近的问题。

更新:

我已将问题缩小到 ImageField。当我从显示的字段中删除它时,保存时不会出现问题。

我正在使用自定义管理表单,但这似乎不是问题所在。我已经尝试使用默认值,但它仍然会发生。我一直在寻找存储 class 中的异常,但没有找到任何异常。我一路剥离它,错误仍然存​​在。

我在 Firefox Developer Tools 中检查了本地 302 POST 和生产 404 POST,它们几乎相同,但生产服务器是 Apache,x-powered-by 是 Phusion Passenger,而本地服务器是 WSGIServer/0.2 CPython/3.7.3.

我实际上已经注意到 404 与现在生产中的其他 multipart/form-data 表单一起出现,这是我以前从未遇到过的。

我不知道问题是什么,但如果你没有日志,运行 它在产品上使用 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 相应)

并检查日志。

或者只使用哨兵。他们有免费套餐

我从来没有完全弄清楚到底发生了什么,但我确实设计了一个解决方法,至少可以让表单正常运行,正如我在 my answer to a related question:

中讨论的那样
  1. Edit _get_response(self, request) in django.core.handlers.base. Change resolver_match = resolver.resolve(request.path_info) to resolver_match = resolver.resolve(request.path).

  2. Add this middleware (adjust to your exact needs):

     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
    

Not ideal by any means, but works for me at the moment.

You can read more details here: https://medium.com/@mnydigital/how-to-resolve-django-admin-404-post-error-966ce0dcd39d

我相信这些问题至少部分是由 ModSecurity Apache 引起的。在托管服务提供商后来重新配置后,我已经解决了一些问题。