Images from ImageField dead in html when project is in production/deployment mode (debug = False)

I have an ImageField model in my database and it is working just fine.
I am making an url for the image in html on a class-based view.
This image will show when debug = True in settings (project in debug mode). The image however, is always dead when I turn debug to False.

I am thinking about a lot of things now seeing as the documentation is painfully vague as usual. Could it be that I need image validation? Security validation?
I have tried the csrf_exempt decorator on my view, this didn’t solve the issue.

If you would have seen something like this before, it would be great if you would know of how to get passed this conundrum?

This is almost certainly a configuration issue.

Start with the basics - please post the model, view, and template involved here.

Show an example of one of the img tag src attributes of a field that is failing. Then, using the Django shell, show the url attribute for that field.

Are these files “Media” files that have been uploaded?

Describe your production environment in detail - what server are you running, what operating system, etc, etc.

Also include the “MEDIA” related settings from your settings file.

Hi Ken - thanks for taking time to reply. I shall provide all of this info tomorrow. I must go sleep now (if I can).

Here is how I have things:

ImageField model (in models.py):

image_upload = models.ImageField(upload_to="images/", null=True, blank=True, validators=[FileExtensionValidator(allowed_extensions=['png', 'jpg'])])

Settings.py

MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # the path becomes [project dir]\media\
MEDIA_URL = '/media/'

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = "/static/"

VALID_IMAGE_EXTENSIONS = [
    ".jpg",
    ".jpeg",
    ".png",
    ".gif",
]

def valid_url_extension(url, extension_list=VALID_IMAGE_EXTENSIONS):
    return any([url.endswith(e) for e in extension_list])

html markup with the image upload (using django ModelForm)

the form html options:
<form action="" method="post" class="row g-3 pb-4" enctype="multipart/form-data">

getting the data with the form:

<div class="card">
<div class="card-header">
		{{ form.image_upload.label }} : 
     </div>
		
      <div class="card-body">      
	     {{form.image_upload}}
    </div>
</div>    

Form class (showing relevant code, bootstrap class in widget):

class RecordForm(ModelForm):
  
    class Meta:
        model = DashboardModel
        
    fields = (
        "image_upload",
    )
        
    labels = {
      "image_upload" : "Images",
    }
        
        
    widgets = {
      
	 "image_upload" : forms.ClearableFileInput(attrs={'class':'form-control form-control-lg', 'placeholder':'images' }),
    }
        

html markup for the image display (using bootstrap classes):

     <div class="row">
                 <div class="col-sm-3">
                    <p class="mb-0">images</p>
                  </div>
                      <div class="col-sm-9">
                            <p class="text-muted mb-0">
                                {%if record.image_upload %}                 
                                <img src="{{ record.image_upload.url }}"  height='430'>
                                {% endif %}
                           </p>
                 </div>
             </div>

urls.py within my app called “dashboard” (showing relevant paths):

urlpatterns = [

    path('dashboard/record_create/', RecordCreatePage.as_view(),  name= 'record_create'),

    path('dashboard/record_details/<str:pk>/', RecordDetailPage.as_view(), name="record_detail"),

    path('dashboard/record_update/<str:pk>/', RecordUpdatePage.as_view(),  name= 'record_update'),

    
]+static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

urls.py outside of app (in project name folder):

urlpatterns = [
    
    path('admin/', admin.site.urls),
    path('', include('dashboard.urls')),

]+static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

I am using:
Windows 10
Python 3.7.0
Pillow 8.2.0

now showing some images, and console screenshots:

Everything is looking correct.

For people who were using a function-based view, I’ve seen them do things differently, they had access to applying more form conditions with request.FILES…I do not know how to do this when using a classed based view.

I just have this in views.py (class-based view):

class RecordDetailPage(LoginRequiredMixin, DetailView):
    
    model = DashboardModel
    template_name = "dashboard/record_detail.html"
    context_object_name = 'record'

Dead image when Debug = False:

image

Same URL working when Debug = True:

Fortunately, your screenshot answered this question:

I can see you’re currently using runserver.

Try using the --insecure parameter.

And keep in mind that in a production mode, you don’t want Django to be serving static or media files. They should be served by the web server directly. (Which also means that in that production environment, you really don’t want your media directory to be in your project directory.)

soon the plan is to deploy this on an azure instance, I was thinking django could be blocking the image files for security purposes. I tried:

python manage.py runserver --insecure

Although it didn’t seem to change the dead image issue.

Just to be sure - I don’t need to override this CreateView method “post”?

None of these other issues you keep raising are in any way relevant to this 404 issue with the images.

The browser is issuing a request - GET /media/images/abc.jpg.

It’s a request for a file.

It’s not a reference to a view.

None of that is at all in play here.

You’re directing runserver to retrieve a file. In a production environment, Django would not even be involved in this request.

The 404 error is the message saying that runserver can’t (or “chooses not to”) find that file.

Actually, you may be stuck here. I’m not seeing a good solution for serving media files through runserver with debug=False.

Note: Something I did notice - and I’m not sure if it’s relevant in this case - the example in the docs show STATIC_URL as being defined without a leading slash. You could try changing STATIC_URL and MEDIA_URL to not have the leading slash.

I get this, I’m just humbled by the fact that django has so many modes and settings abstracted in the class-based view setup to the point where I’m thinking I’ve left something undone. I am also relatively new to this framework. I wish there was more information about “debug” in settings and what it really means when it is false vs. true. I’m overthinking it now and thinking that I need security validation/form validation or more.
I also have never deployed something on an azure instance, this will be a first, so I can’t tell what will happen

You may - but if so, it would be for different reasons - it would have nothing to do with this.

What you may or may not have left un-done does not affect this issue. THERE IS NO VIEW ASSOCIATED WITH THIS PROBLEM

Please stop tying yourself in knots about this. You’re looking at so many different topics other than what needs to be addressed for this issue.

1 Like

the template page is connected to a DetailView class. So I wasn’t even sure whether the issue related to a view or not. Had I not asked you, I wouldn’t know eitherway, but I’m taking your word for it that it’s a server related thing

I wasn’t confident about anything seeing as we don’t get to know what Debug parameter is really doing.
I made a mock deployment on heroku, but the same issue occurred, this is a complicated issue as I am in no way knowledgeable about appllication deployments.

But as explained above, the retrieval of an image by the browser is a separate request and has nothing to do with the view that rendered the template containing the url to be requested.

Your runserver log shows this. No need to take my word for any of it.

Actually, you do. You have the complete Django source code available to you. If you want to find out everything affected by the debug setting, it’s all in there.

What I can definitively tell you is that the debug setting is not going to change the behavior of your code.

That’s yet a different issue. User-uploaded files and heroku are apparently a common problem - the two don’t play nicely together. There are at least two other threads here discussing that topic that I can remember. (I don’t use heroku so I didn’t really pay close attention to it.)

This is probably the most complex topic with Django because there are so many options and possibilities and so little of it deals with Django itself compared to its relationship to all the external components it needs to interface with.

A typical deployment for us involves at least 8 separate processes.

Thanks for the insight. I would feel worse if I didn’t chat to anyone about this matter. I will do what I can.

I should have said that my entire database is connected to a postgres resource on heroku, I had sort of forgotten that too. So I might have no persistent image storage location set up on this cloud. It’s over my head right now, but I found something interesting here:

the plan eitherway is to use an Azure instance, so I will focus on that (I have some learning to do).

Interesting to know, but does not affect this situation. There’s no database reference involved in retrieving a static or media file. (Well, there are some unusual cases where there could be, but there’s nothing in anything you’ve posted that indicate that any of those edge cases apply.)

I can only guess that the 404 is likely due to my ALLOWED_HOSTS setting. I’m putting in localhost, as well as the url to my heroku app - I tried different combinations, but I keep getting the 404 as it is probably seeking an extra configuration. If I did keep everything locahost with the default SQLite database, the problem may not be there (I’m surmising however).

Which 404?

Nope, not the issue here. That would cause a different error.

Nope, that’s not going to solve it either.

This original problem is fundamentally caused by using runserver with debug = False. It’s just not designed to be used that way.

You definitely do not want to use it when deploying your app to a public / production environment. Your deployment environment should use something like gunicorn or uwsgi for that.

Start at Deploying Django | Django documentation | Django and work your way forward from there.

1 Like