Why does upload_to in ImageField not working at all when i try to save uploaded image from user in views

Viewed 220

I am trying to make a simple mail service with very simple architecture project. At this moment my backend part of development is almost done, but i have stoped already fro two days because of i have no idea why does images saving not working.

Obviously, i expect that user upload his profile avatar and it's saving to path i've set in upload_to. Path to image must looks like profiles/UserName/his_profile_img.jpeg

For last two days i have spent more than 12 hours by trying to solve it.

  1. This is my model where is my trouble ImageField:
from django.db import models
from datetime import datetime, timedelta
from django.contrib.auth.models import User


def username(user, file):
    string_username = str(user).split()[0]
    return f"profiles/{string_username}/{file}"


class Profile(models.Model):
    user                 = models.OneToOneField(User, on_delete=models.CASCADE, related_name="Profile")

    ##### This is here
    avatar               = models.ImageField(default="/profiles/profile.png", upload_to=username)
    
    name                 = models.CharField(max_length=30, blank=True)
    surname              = models.CharField(max_length=30, blank=True)

    email                = models.EmailField(max_length=64, blank=True)
    bio                  = models.CharField(max_length=1024, blank=True)

    def username(self):
        return self.user.username

    @property
    def joined(self):

        joined = self.user.date_joined.strftime("%d %B %Y")

        is_today = joined == datetime.today().strftime("%d %B %Y")
        if is_today:
            return "today"

        is_yesterday = joined == datetime.date(datetime.today() - timedelta(days=1)).strftime("%d %B %Y")  
        if is_yesterday:
            return "yesterday"

        return f"{joined}"

    def __str__(self):
        return f"{self.user} profile"
  1. This is a view function, which doesn't save image:
def my_profile(request):

    if not request.user.is_authenticated:
        return redirect('login') 

    user_profile = Profile.objects.get(user=request.user)
    sent_messages = Mail.objects.filter(from_user_inf=request.user).count()
    received_messages = Mail.objects.filter(to_user_inf=request.user).count()

    context = {
        "profile": user_profile,
        "sent": sent_messages, 
        "received": received_messages
    }
    if request.method == 'POST':
        user_profile.name      = request.POST.get("name")
        user_profile.surname   = request.POST.get("surname")
        user_profile.email     = request.POST.get("email")
        user_profile.bio       = request.POST.get("bio")

        avatar = request.FILES.get("avatar")

        # I've also tried get image by:
        ##### request.POST.get("avatar")
        ##### saving image by "with open" command(this is too weird)

        if avatar:
            user_profile.avatar = avatar
            
        user_profile.save()
        messages.info(request, "Saved successfully !")
        return render(request, 'my_profile.html', context=context)

    return render(request, 'my_profile.html', context=context)

  1. And my_profile.html file and a piece of code where i set image uploading:
<div>
    <img class="img" src="{{ profile.avatar.url }}" width="150" height="150">
    <label for="file" cursor="pointer" class="btn">Change Photo</label>
    <input id="file" style="visibility:hidden;" name="avatar" type="file" accept="image/*">
</div>

Yes, i did correct settings and added it to urls.

  1. settings.py ---> ROOT and URL media, static settings :
STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'static'

MEDIA_ROOT = BASE_DIR / 'media'
MEDIA_URL = '/media/'
  1. urls.py:
urlpatterns = [
    ### There are my urls
]

if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Thanks for attention, hope we can solve it !

1 Answers

It's easy with ModelForms:

# forms.py

from django import forms
# import your model here

class ProfileForm(forms.ModelForm):
    
    class Meta:
        model = Profile
        fields = ('name', 'surname', 'email', 'bio', 'avatar')


# views.py
# imports...


def my_profile(request):

    if not request.user.is_authenticated:
        return redirect('login') 

    user_profile = Profile.objects.get(user=request.user)
    sent_messages = Mail.objects.filter(from_user_inf=request.user).count()
    received_messages = Mail.objects.filter(to_user_inf=request.user).count()

    context = {
        "profile": user_profile,
        "sent": sent_messages, 
        "received": received_messages
    }
    if request.method == 'POST':
        form = ProfileForm(
            instance=user_profile, data=request.POST, files=request.FILES
        )
        if form.is_valid():
            form.save()
            messages.info(request, "Saved successfully !")
            # Best use a redirect to a success page here.
            # Otherwise, if the user clicks reload, the form will be resent.
            return render(request, 'my_profile.html', context=context)
    else:
       form = ProfileForm(instance=user_profile)

    # Use the form in the template! @see: https://docs.djangoproject.com/en/3.2/topics/forms/#the-template
    # If you need personalize for bootstrap, etc @see: https://github.com/django-crispy-forms/django-crispy-forms
    context['form'] = form
    return render(request, 'my_profile.html', context=context)


If you need upload the file without the ModelForm, manually save the file to a server path and then set this path (relative to MEDIA_ROOT) in the model. See the handle_uploaded_file in this link https://docs.djangoproject.com/en/3.2/topics/http/file-uploads/#basic-file-uploads.

Related