forms.ImageField is not working

Hi everyone, I have a forms.Imagefield a in model form for uploading a photo to a database
but after choosing a photo to upload, it is not saving it to the database as the rest of the form input field.
here is the line in the forms

 	image = forms.ImageField(label="upload product image", required=False)

and here is the corresponding model field

image = models.ImageField(upload_to='uploads/product/')

what is possibly causing such a problem

I tried do replicate this. It worked for me.

Few followups.

  1. When you say the image is not saving to database, do you mean that trying to open the image url gives 404 or is the image field empty?
  2. Can you give more details about the form and view as well.

First, I suggest you review the docs at File Uploads | Django documentation | Django. This covers what you need to do in both the HTML form and in the view to handle uploaded files.

Also, keep in mind that files do not get saved in the database. The file field only contains the file name. The files get stored in the MEDIA_ROOT directory (or in a location defined by the storage class).

For us to be able to provide more specific assistance, you will need to post your view and your template involved in this process. (It may also be helpful to see the MEDIA_ROOT setting.)

1 Like

@KenWhitesell @anilrai21
I really appreciate you help
I would like to clarify that I’m saving the photos to the MEDIA folder through using the corresponding settings in the settings.py and urls.py as you can see here

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

where BASE_DIR is

BASE_DIR = Path(__file__).resolve().parent.parent
 

for the urls.py

from django.contrib import admin
from django.urls import path, include
from . import settings
from django.conf.urls.static import static 

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('store.urls')),
    path('cart/', include('cart.urls')),
    path('payment/', include('payment.urls'))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

AND I’m using 2 views for the PRODUCT model
the first one is to add new product

     if request.user.is_authenticated:
          user = Profile.objects.get(user=request.user.id)
          shops = Vendor.objects.filter(id = request.user.id)
          product_form = Product_Form(request.POST or None , request.FILES,  request=request)# , initial={'vendor': vendor.id}) 
          if request.method == 'POST':
               if product_form.is_valid():
                    product_form.save()
                    messages.success(request, 'You Added a new Product successfuly')
                    return redirect('update_user')
               else:
                    for error in list(product_form.errors.values()):
                         print(request.body)
                         messages.error(request, error)
                         return redirect('update_user')
          else:     
               return render(request, "add_new_product.html", {"product_form": product_form} )
     else:
          messages.success(request, "You Must be Logged in to access this page")
          return redirect('home')

this views works fine and it adds new photo for each new product
the problem is with the update product view

def edit_product(request, id):
     if request.user.is_authenticated:
          product = Product.objects.get(id=id)
          shop_id = product.vendor.id
          # Prevent other users from accessing current user data
          if request.user == product.vendor.owner.user:

               # print(shop_id)
               print(request.FILES)
               product_form = Product_Form(request.POST or None,  instance=product,  request=request) 
               if request.method == 'POST':
                    if product_form.is_valid():
                         product_form.save()
                         messages.success(request, "Shop has Been Updated")
                         return redirect('update_user')
               
                    else:
                         for error in list(product_form.errors.values()):
                              print(request.body)
                              messages.error(request, error)
                              return redirect('update_user')
               else:
                    return render(request, "edit_product.html", {"product_form": product_form, 'shop_id': shop_id})
          else:
               messages.success(request, "You are UNAUTHORIZED to access this page")
               return redirect('home')

     else:
          messages.success(request, "You Must be Logged in to access this page")
          return redirect('home')

the main difference between the 2 views is that the second one I use an existing product instance to fill in the form
so I use

               product_form = Product_Form(request.POST or None,  instance=product,  request=request) 

when I used it like that

          product_form = Product_Form(request.POST or None ,  instance=product,  request.FILES,  request=request)

the instance data is lost completely

and here are the form class

class Product_Form(forms.ModelForm):
	name = forms.CharField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' enter your product name'}), required=True , error_messages = {'required':"Please Enter your Name1"})
	price = forms.DecimalField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' price'}), required=True , error_messages = {'required':"Please Enter your Name2"})
	category = forms.ModelChoiceField(label="category",queryset=Category.objects.all() , required=True,  error_messages = {'required':"Please Enter your Name3"})
	# vendor = forms.CharField(label="vendor", widget=forms.Select(attrs={'class':'form-control', 'disabled': 'disabled'}), required=False)
	description = forms.CharField(label="description", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' description'}), required=True , error_messages = {'required':"Please Enter your Name4"})
	image = forms.ImageField(label="upload product image", required=False,  error_messages = {'required':"Please Enter your Name5"})
	is_sale = forms.BooleanField(label="is_sale", required=False , error_messages = {'required':"Please Enter your Name6"})
	sale_price = forms.DecimalField(label="sale_price", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' sale price'}), required=True , error_messages = {'required':"Please Enter your Name7"})
	is_not_available = forms.BooleanField(label="is_not_available", required=False , error_messages = {'required':"Please Enter your Name8"})

	def __init__(self, *args, **kwargs):
			self.request = kwargs.pop("request")
			super(Product_Form, self).__init__(*args, **kwargs)
			owner = Profile.objects.get(user=self.request.user)
			self.fields["vendor"].queryset = Vendor.objects.filter(owner = owner)

	class Meta:
		model = Product
		fields = ("name", "price", "category", 'vendor' , "description", "image", "is_sale", "sale_price", "is_not_available")

here is the tempate

{% extends 'base.html' %}



{% block content %}

        <!-- Header-->
        <header class="bg-dark py-5">
            <div class="container px-4 px-lg-5 my-5">
                <div class="text-center text-white">
                    <h1 class="display-4 fw-bolder">Shop in style</h1>
                    <p class="lead fw-normal text-white-50 mb-0">With this shop hompeage template</p>
                </div>
            </div>
        </header> 
        <br><br><br> 
            <div class="container">
            <div class="card mb-3" >
                <div class="row g-0">
                  <div class="col-md-4">
                    {% if product.image%}
                    <img src="{{ product.image.url }}" class="img-fluid rounded-start" alt="{{ product.name }}">
                    {% endif %}
                    <center>
                    <div class="col-md-6">
                        <div class="rating-card p-4">
                            <div class="star-rating animated-stars ">
                                <label for="star5" class="bi bi-star-fill"></label>
                                <label for="star5" class="bi bi-star-fill"></label>
                                <label for="star5" class="bi bi-star-fill"></label>
                                <label for="star5" class="bi bi-star-fill"></label>
                                <label for="star5" class="bi bi-star-fill"></label>

                            </div>
                        </div>
                    </div>
                </div>
                </center>
                    <div class="col-md-8">
                        <div class="card-body">
                            <center>
                            <h5 class="card-title">{{ product.name }}</h5>
                            <p class="card-text">{{ product.desciption }}</p>
                            <br><br>
                            {% if product.is_sale %}
                            <div class="d-flex justify-content-center small text-warning mb-2" >
                                <div class="bi-star-fill"></div>
                                &nbsp;&nbsp;Sale&nbsp;&nbsp;
                                <div class="bi-star-fill"></div>
                            </div>
                            <strike>{{ product.price }} L.E</strike>
                            &nbsp;
                            {{ product.sale_price }} L.E
                            {% else %}
                            <p class="card-text">price:    {{ product.price }} L.E </p>

                            {% endif %}
                            <br><br>
                            <br><br>
                            <div class="row justify-content-center">
                            <div class="col-md-2">Quantity:</div>
                            <div class="col-md-2">
                                <select class="form-select form-select-sm" id="qty-cart" >
                                    <option value="1">1</option>
                                    <option value="2">2</option>
                                    <option value="3">3</option>
                                    <option value="4">4</option>
                                    <option value="5">5</option>
                                    <option value="6">6</option>
                                </select>
                            </div>  
                            </div>
                            <br><br><br><br><br><br><br>
                            <a href="{% url 'home' %}" class="btn btn-secondary">Go to Home</a>
                            <button type="button" class="btn btn-secondary"  id="add-to-cart"  value="{{product.id}}">Add to Cart</button>
                        </center>
                        </div>
                  </div>
                </div>
            </div>
        </div>            
        {% if user.is_authenticated %}
        <center>
                <form method="POST" action="{% url 'product' product.id  %}" >
                    {% csrf_token%}
                    <div class="form-group">

                    <div class="col-md-6">
                        <div class="rating-card p-4">
                            <h5 class="mb-4" >التقييم </h5>
                            <div class="star-rating animated-stars">
                                <input type="radio" id="star5" name="rating" value="5">
                                <label for="star5" class="bi bi-star-fill"></label>
                                <input type="radio" id="star4" name="rating" value="4">
                                <label for="star4" class="bi bi-star-fill"></label>
                                <input type="radio" id="star3" name="rating" value="3">
                                <label for="star3" class="bi bi-star-fill"></label>
                                <input type="radio" id="star2" name="rating" value="2">
                                <label for="star2" class="bi bi-star-fill"></label>
                                <input type="radio" id="star1" name="rating" value="1">
                                <label for="star1" class="bi bi-star-fill"></label>
                            </div>
                            <div class="">
                                <div class="card">
                                    <div class="card-header">
                                        Write your review 
                                    </div>
                                    <div class="card-body">
                                            <input type="text" class="form-control"  placeholder="Write your review" name="review">
                                            </div>
                                            <br>
                                            <button type="submit" class="btn btn-secondary">Click to Submit</button>
                                        </form>
                                    </div>
                                </div>
                        </div>
                    </div>
        </center>
        {% endif %}
        <center>
            <br><br><br>
            <h3>Customers Reviews</h3>
            {% if reviews  %}
               {% for review in reviews %}
        <div class="container">
        <div class="comments border border-light rounded bg-light text-dark"> 
         <p class="font-weight-bold"> 
           {{ review.name }} 
           <span class=" text-muted font-weight-normal"> 
             {{ review.created_on }}
            </span>
          </p>
          {{ review.body | linebreaks }}
        </div>
        <br>
        </div>
        {% endfor %}
        </center>
        {% elif not user.is_authenticated %}
        <p>لا توجد تققيمات لهذا المنتج , برجاء تسجيل حساب و كن اول من يكتب  تقييم لة  </p>
        {% else %}
        <p>لا توجد تققيمات لهذا المنتج , كن اول من يكتب  تقييم لة  </p> 
        {% endif %}
            <style>
                .star-rating {
                    direction: rtl;
                    display: inline-block;
                    cursor: pointer;
                }

                .star-rating input {
                    display: none;
                }

                .star-rating label {
                    color: #ddd;
                    font-size: 24px;
                    padding: 0 2px;
                    cursor: pointer;
                    transition: all 0.2s ease;
                }

                .star-rating label:hover,
                .star-rating label:hover~label,
                .star-rating input:checked~label {
                    color: #ffc107;
                }
            </style>

            <script>
                document.querySelectorAll('.star-rating:not(.readonly) label').forEach(star => {
                    star.addEventListener('click', function() {
                        this.style.transform = 'scale(1.2)';
                        setTimeout(() => {
                            this.style.transform = 'scale(1)';
                        }, 200);
                    });
                });
            </script>
            <br><br><br><br><br><br><br><br><br><br>
            <script>
                // check if a button presses
                    $(document).on('click', '#add-to-cart', function(e){
                        e.preventDefault();
                        $.ajax({
                            type: 'POST',
                            url: '{% url "cart_add" %}',
                            data: {
                                product_id: $('#add-to-cart').val(),
                                product_qty:$('#qty-cart option:selected').text(),
                                csrfmiddlewaretoken: '{{ csrf_token }}',
                                action:'post',
                            },

                            success: function(json){
                                console.log(json)
                                document.getElementById("cart_quantity").textContent = json.qty
                                location.reload;
                            },

                            error: function(xhr, errmsg, err){
                                
                            }
                        });
                        

                    })

            </script>

            
{% endblock %}

and here is the product model

class Product(models.Model):
     name = models.CharField(max_length=100)
     price = models.DecimalField(default=0, decimal_places=2, max_digits=6)
     category = models.ForeignKey(Category ,on_delete=models.CASCADE )
     vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)
     description = models.CharField(max_length=250, default='', blank=True, null=True)
     image = models.ImageField(upload_to='uploads/product/')
     is_sale = models.BooleanField(default=False)
     sale_price = models.DecimalField(default=0, decimal_places=2, max_digits=6)
     is_not_available = models.BooleanField(default=False)

     def __str__(self):
          return self.name

I’m surprised that this line doesn’t throw an error. You have the instance=product before the request.FILES instead of after.

You also don’t have the enctype="multipart/form-data" on your form element. (See Basic File Uploads)

Also, you don’t have your html for the form properly structured. Inside your <form element you have 8 <div> elements but only 3 </div> elements.

@KenWhitesell
you are correct this line of code gives an error

          product_form = Product_Form(request.POST or None ,  instance=product,  request.FILES,  request=request)

but when I rearranged like that

          product_form = Product_Form(request.POST or None , request.FILES,   instance=product,  request=request)

it results in an empty form without the product instance data

and regarding the template
I think I posted a mistaken template
here are the corresponding template

{% extends 'base.html' %}



{% block content %}

        <!-- Header-->
        <div id="user-info" data-user-id="{{ shop_id }}"></div> 
        <header class="bg-dark py-5">
            <div class="container px-4 px-lg-5 my-5">
                <div class="text-center text-white">
                    <h1 class="display-4 fw-bolder">Edit current Product</h1>
                    <p class="lead fw-normal text-white-50 mb-0"></p>
                </div>
            </div>
            </header>
            
            <div class="container">
                <div class="row">
                    <center>
                        <div class="col-8">


                            <br><br>
                            {% if user.is_authenticated %}      
                                <form method="POST", action="" enctype="multipart/form-data">
                                {% csrf_token %}
                                {{ product_form.as_p}}
                                <strong>Edit Product</strong>

                                <button type="submit" class="btn btn-secondary">edit product </button>
                                </form>
                            {% endif  %}  
                        </div>
                    </center>
                </div>
            </div>
    <script>

Please post your current corrected view.


def edit_product(request, id):

 if request.user.is_authenticated:

      product = Product.objects.get(id=id)

      shop_id = product.vendor.id

      \# Prevent other users from accessing current user data

      if request.user == product.vendor.owner.user:



           \# print(shop_id)

           print(request.FILES)

           product_form = Product_Form(request.POST or None,  request.FILES, instance=product) 

           if request.method == 'POST':

                if product_form.is_valid():

                     product_form.save()

                     messages.success(request, "Shop has Been Updated")

                     return redirect('update_user')

           

                else:

                     for error in list(product_form.errors.values()):

                          print(request.body)

                          messages.error(request, error)

                          return redirect('update_user')

           else:

                return render(request, "edit_product.html", {"product_form": product_form, 'shop_id': shop_id, 'product':product})

      else:

           messages.success(request, "You are UNAUTHORIZED to access this page")

           return redirect('home')



 else:

      messages.success(request, "You Must be Logged in to access this page")

      return redirect('home')

```

this view results in an empty form where there are no data in the form field though I entered a product instance

instance=product

the data appears only when I remove this part

request.FILES

but in such case the ability to update the image is lost

Please post the complete error message you’re getting.

If there is no error message being generated on the console, please post the current version of your Product_Form.

if you can add it html form tag it could work

@KenWhitesell

there are no error messages ,

here are the Product_form


class Product_Form(forms.ModelForm):

name = forms.CharField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' enter your product name'}), required=True , error_messages = {'required':"Please Enter your Name1"})

price = forms.DecimalField(label="", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' price'}), required=True , error_messages = {'required':"Please Enter your Name2"})

category = forms.ModelChoiceField(label="category",queryset=Category.objects.all() , required=True,  error_messages = {'required':"Please Enter your Name3"})

\# vendor = forms.CharField(label="vendor", widget=forms.Select(attrs={'class':'form-control', 'disabled': 'disabled'}), required=False)

description = forms.CharField(label="description", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' description'}), required=True , error_messages = {'required':"Please Enter your Name4"})

image = forms.ImageField(label="upload product image", required=False,  error_messages = {'required':"Please Enter your Name5"})

is_sale = forms.BooleanField(label="is_sale", required=False , error_messages = {'required':"Please Enter your Name6"})

sale_price = forms.DecimalField(label="sale_price", widget=forms.TextInput(attrs={'class':'form-control', 'placeholder':' sale price'}), required=True , error_messages = {'required':"Please Enter your Name7"})

is_not_available = forms.BooleanField(label="is_not_available", required=False , error_messages = {'required':"Please Enter your Name8"})



def \__init_\_(self, \*args, \*\*kwargs):

        self.request = kwargs.pop("request")

        super(Product_Form, self).\__init_\_(\*args, \*\*kwargs)

        owner = Profile.objects.get(user=self.request.user)

        self.fields\["vendor"\].queryset = Vendor.objects.filter(owner = owner)



class Meta:

    model = Product

    fields = ("name", "price", "category", "vendor", "description", "image", "is_sale", "sale_price", "is_not_available")

and here is the Product_model

class Product(models.Model):

 name = models.CharField(max_length=100)

 price = models.DecimalField(default=0, decimal_places=2, max_digits=6)

 category = models.ForeignKey(Category ,on_delete=models.CASCADE )

 vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE)

 description = models.CharField(max_length=250, default='', blank=True, null=True)

 image = models.ImageField(upload_to='uploads/product/')

 is_sale = models.BooleanField(default=False)

 sale_price = models.DecimalField(default=0, decimal_places=2, max_digits=6)

 is_not_available = models.BooleanField(default=False)



 def \__str_\_(self):

      return self.name 

the html tag is already added

Side note:

When posting code using the three backticks, the backticks must be lines by themselves and not part of a line. (I’ve taken the liberty of fixing your previous post.)

I find it difficult to believe that you’re not getting any error message associated with this form, because you have:

But your call to the form is not passing request as a parameter to that form.

@KenWhitesell

Ok, thank you

@KenWhitesell

yes this code does gives an error

I corrected it too

def edit_product(request, id):
     if request.user.is_authenticated:
          product = Product.objects.get(id=id)
          shop_id = product.vendor.id
          # Prevent other users from accessing current user data
          if request.user == product.vendor.owner.user:

               # print(shop_id)
               product_form = Product_Form(request.POST or None,  instance=product,  request=request) 
               if request.method == 'POST':
                    product_image = request.FILES['image']
                    if product_form.is_valid():
                         product_form.save()
                         messages.success(request, "Shop has Been Updated")
                         return redirect('update_user')
               
                    else:
                         for error in list(product_form.errors.values()):
                              print(request.body)
                              messages.error(request, error)
                              return redirect('update_user')
               else:
                    return render(request, "edit_product.html", {"product_form": product_form, 'shop_id': shop_id, 'product':product})
          else:
               messages.success(request, "You are UNAUTHORIZED to access this page")
               return redirect('home')

     else:
          messages.success(request, "You Must be Logged in to access this page")
          return redirect('home')

now the error has gone but still I can’t update the image for that product instance as the request.FILES can not be passed in the product_form constructor

What makes you think that? What is the code you are trying, and what error does it give?

I found I work around after a lot of trial and error ,

I didn’t pass request.FILES as usual but I used

                    if 'image' in request.FILES:
                         product_image = request.FILES['image']

here is the final version of the view and it works fine and is updating the image,

def edit_product(request, id):
     if request.user.is_authenticated:
          product = Product.objects.get(id=id)
          shop_id = product.vendor.id
          # Prevent other users from accessing current user data
          if request.user == product.vendor.owner.user:

               # print(shop_id)
               product_form = Product_Form(request.POST or None,  instance=product,  request=request) 
               if request.method == 'POST':
                    # get product vendor 
                    owner = Profile.objects.get(user=request.user)
                    # product_image = request.FILES['image']
                    ## check existence of a new product photo
                    if 'image' in request.FILES:
                         product_image = request.FILES['image']
                         if product_form.is_valid():
                              edited_product = product_form.save(commit=False)
                              edited_product.image = product_image
                              edit_product.vendor = Vendor.objects.get(id = shop_id)
                              edited_product.save()
                              messages.success(request, "Shop has Been Updated")
                              return redirect('update_user')
                    
                         else:
                              for error in list(product_form.errors.values()):
                                   print(request.body)
                                   messages.error(request, error)
                                   return redirect('update_user')
                    else:
                         if product_form.is_valid():
                              edited_product = product_form.save(commit=False)
                              # edited_product.image = product_image
                              edit_product.vendor = Vendor.objects.get(id = shop_id)
                              edited_product.save()
                              messages.success(request, "Shop has Been Updated")
                              return redirect('update_user')
                    
                         else:
                              for error in list(product_form.errors.values()):
                                   print(request.body)
                                   messages.error(request, error)
                                   return redirect('update_user')

               else:
                    return render(request, "edit_product.html", {"product_form": product_form, 'shop_id': shop_id, 'product':product})
          else:
               messages.success(request, "You are UNAUTHORIZED to access this page")
               return redirect('home')

     else:
          messages.success(request, "You Must be Logged in to access this page")
          return redirect('home')