Django admin 404 error when creating or editing a model instance

家住魔仙堡 提交于 2021-01-03 05:36:14

问题


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:

  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

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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!