tags:

views:

772

answers:

3

Hello,

I've been running into a problem while trying to delete uploaded images.

The error is along these lines:

SuspiciousOperation: Attempted access to '/media/artists/12-stones/154339.jpg' denied.

After reading around it looks like the error is due to the fact that it's looking for the image in the wrong place (notice first slash, /media/ doesn't exist on the filesystem)

My MEDIA_ROOT and MEDIA_URL are:

MEDIA_ROOT = '/home/tsoporan/site/media/'
MEDIA_URL = "/media/

My models upload_to parameter is passed this function:

def get_artist_path(instance, filename):
  return os.path.join('artists', slugify(instance.name), filename)

My questions are:

1) How can I fix this problem for future uploads?

2) Is it possible to fix my current images' paths without having to reupload?

Regards, Titus

+4  A: 

Well, a little grepping around in the code shows that there may be a deeper error message that got homogenized along the way.

in django/core/files/storage.py, line 210 (this is in 1.1.1) we have:

def path(self, name):
    try:
        path = safe_join(self.location, name)
    except ValueError:
        raise SuspiciousOperation("Attempted access to '%s' denied." % name)
    return smart_str(os.path.normpath(path))

So the error has to be coming out of safe_join().

In django/utils/_os.py, we have the following. Note the ValueError it throws on the third to last line:

===========================

def safe_join(base, *paths):
    """
    Joins one or more path components to the base path component intelligently.
    Returns a normalized, absolute version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).
    """
    # We need to use normcase to ensure we don't false-negative on case
    # insensitive operating systems (like Windows).
    base = force_unicode(base)
    paths = [force_unicode(p) for p in paths]
    final_path = normcase(abspathu(join(base, *paths)))
    base_path = normcase(abspathu(base))
    base_path_len = len(base_path)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is os.sep (or nothing, in which case final_path must be
    # equal to base_path).
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len+1] not in ('', sep):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path

==================

Hmmm, "The joined path is located outside of the base path component". Now there are a couple of calls to abspathu() in there (which is defined just above this routine and is different for NT than for other OSes). abspathu() converts all non-absolute paths to absolute by tacking on os.cwdu(), the current working directory.

Question: By any chance do you have a symlink (symbolic link) to your media directory? In other words, it's not a direct child of the project directory? I don't know if this is a valid question, it just popped out of my head.

Question: What are the values of self.location and name that are being passed to safe_join()?

Wild-ass-guess: is self.location empty?

Another wild-ass-guess: did MEDIA_ROOT somehow get changed to /media/?

If you have your own copy of Django installed (it's not hard to do), trying putting some print statements in these routines and then run it as the development server. The print output will go to the console.

Update: Hmmm. You said "2) The values for self.location and name are: /home/tsoporan/site/media and /media/albums/anthem-for-the-underdog/30103635.jpg"

Does the following path make any sense?

"/home/tsoporan/site/media/media/albums/anthem-for-the-underdog"

Note the .../media/media/... in there.

Also, what OS is this? Django rev?

Peter Rowell
1) No I do not have a symbolic link to my media directory. 2) The values for self.location and name are:/home/tsoporan/site/media and /media/albums/anthem-for-the-underdog/30103635.jpg -- (this is for an album but the problem is the same for artists or albums)3) self.location isn't empty.4) MEDIA_ROOT is still the same I believe.
tsoporan
Note my update above. It would have been a little tricky formatting it in a comment.
Peter Rowell
A: 

Ah figured it out, slightly embarrassing, but it turns out the error was higher up. I was plugging these images in by a script and while going over it again realized that my paths started with /media/.

Now I have about 4000 images with wrong paths ... is there a way to somehow amend the paths for all these images? Or will the need to be reuploaded?

Thanks everyone, apologies for my mistake.

tsoporan
See my answer below ...
Peter Rowell
A: 

You really should just ask a new question on this. Try the following:

Write a standalone django script that looks something like this:

from django.core.management import setup_environ
from mysite import settings
setup_environ(settings)
from django.db import transaction

from app.models import Album # or whatever your model name is

for a in Album.objects.all():
    # Do something to cleanup the filename.
    # NOTE! This will not move the files, just change the value in the field.
    a.filename = re.sub(r'^/media', '', a.filename)
    a.save()

transaction.commit_unless_managed() # flush all changes
Peter Rowell