问题
I'm currently debugging a weird problem with a Django site where one specific model is triggering a 404 error when creating a new instance or editing an existing one in the admin interface.
Specifically, the error occurs when the form is submitted. I can GET
the changeform just fine.
This is only occuring on the live site and only when saving this model. All other models behave as expected, and when I run it locally, everything works as expected. When created programatically, everything is also fine both live and locally.
Here's my model:
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)
The ContentPage
class is the one triggering the problem in the admin interface. My other class that inherits from Content
works fine.
I have stripped back my admin setup to the following and it is still occuring:
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)
Here's a basic URL config that reproduces the problem:
from myapp.admin import admin_site
urlpatterns = [
path('mycooladminsite/', admin_site.urls),
path('', include('myapp.urls')),
]
A few other things I've checked:
- I have no signals interferring with the save.
- I am not performing any actions in the model's
save()
method. - I have checked for
get_object_or_404()
calls and can't see any that would affect this.
I've spent a few hours digging through this and I'm currently at a brick wall.
The database engine is mysql.connector.django
, with Django version 2.2.11. I can't change the engine or update Django to 3.x yet for this site.
This is a recent problem that I did not notice previously.
Update:
I've narrowed down the problem to the ImageField. When I remove that from the fields displayed, the problem does not occur on save.
I'm using a custom admin form but that doesn't seem to be the problem. I've tried using the default and it still occurs. I've been looking for exceptions in the storage class but haven't found any. I stripped it all the way back and the error remains.
I've examined the local 302 POST
and production 404 POST
in Firefox Developer Tools and they're virtually identical but production server is Apache and x-powered-by
is Phusion Passenger, whereas locally the server is WSGIServer/0.2 CPython/3.7.3.
I've actually noticed a 404 occurring with other multipart/form-data
forms in production now, which I never got before.
回答1:
I don't know what the issue is but if you don't have logs, run it on prod with DEBUG=False, don't use Sentry etc. and it happens only when you save ContentPage
admin form, then you can overwrite save_new
or save_model
method in your ModelAdmin
with
def save_model(self, request, obj, form, change):
try:
obj.save()
except Exception:
logging.exception("blah")
(or save_new
accordingly)
and check logs.
Or just use Sentry. They have a free tier
回答2:
I never fully got to the bottom of what was going on, but I did devise a workaround to at least allow the forms to function correctly, as I discussed in my answer to a related question:
Edit
_get_response(self, request)
indjango.core.handlers.base
. Changeresolver_match = resolver.resolve(request.path_info)
toresolver_match = resolver.resolve(request.path)
.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
I believe these problems may, in part at least, be caused by ModSecurity Apache. Some problems I had went away after the hosting provider later reconfigured this.
来源:https://stackoverflow.com/questions/62796728/django-admin-404-error-when-creating-or-editing-a-model-instance