0

I've been working on a personal photo website built on Django for the last several months and successfully deployed it with AWS. Til now I've done all my development on my Windows 10 desktop. I recently got a new Windows 10 laptop and have gotten stuck setting up a dev environment on it. I cloned my Github repo to my laptop and successfully gotten the site to run locally. However when I go to the admin page to upload a photo and every time I get a SuspiciousFileOperation exception. The joined path is located outside of the base path component. I've also gotten this error when running collectstatic. The settings are identical to my desktop setup, unless I'm overlooking an issue staring right at me.

I think it might have something to do with the way I set up the virtualenv or how my MEDIA variables are set up, though they are the same as what works on my desktop. I've read through the Django documentation but haven't found anything definitive. Or maybe someone could explain a bit more about setting up virtualenv? Does Django/Python need certain permissions I missed setting up on my laptop to move files around?

Below is the full error and the relevant code snippets.

Stack Trace:

    Environment:


    Request Method: POST
    Request URL: http://127.0.0.1:8000/admin/PortfolioApp/image/add/

    Django Version: 2.1.4
    Python Version: 3.7.1
    Installed Applications:
    ['django.contrib.admin',
     'django.contrib.auth',
     'django.contrib.contenttypes',
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
     'PortfolioApp.apps.PortfolioappConfig',
     'storages']
    Installed Middleware:
    ['django.middleware.security.SecurityMiddleware',
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.middleware.common.CommonMiddleware',
     'django.middleware.csrf.CsrfViewMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.middleware.clickjacking.XFrameOptionsMiddleware']



    Traceback:

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\handlers\exception.py" in inner
      34.             response = get_response(request)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\handlers\base.py" in _get_response
      126.                 response = self.process_exception_by_middleware(e, request)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\handlers\base.py" in _get_response
      124.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\options.py" in wrapper
      604.                 return self.admin_site.admin_view(view)(*args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\utils\decorators.py" in _wrapped_view
      142.                     response = view_func(request, *args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\views\decorators\cache.py" in _wrapped_view_func
      44.         response = view_func(request, *args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\sites.py" in inner
      223.             return view(request, *args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\options.py" in add_view
      1637.         return self.changeform_view(request, None, form_url, extra_context)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\utils\decorators.py" in _wrapper
      45.         return bound_method(*args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\utils\decorators.py" in _wrapped_view
      142.                     response = view_func(request, *args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\options.py" in changeform_view
      1525.             return self._changeform_view(request, object_id, form_url, extra_context)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\options.py" in _changeform_view
      1564.                 self.save_model(request, new_object, form, not add)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\contrib\admin\options.py" in save_model
      1091.         obj.save()

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\base.py" in save
      718.                        force_update=force_update, update_fields=update_fields)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\base.py" in save_base
      748.             updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\base.py" in _save_table
      831.             result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\base.py" in _do_insert
      869.                                using=using, raw=raw)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\manager.py" in manager_method
      82.                 return getattr(self.get_queryset(), name)(*args, **kwargs)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\query.py" in _insert
      1136.         return query.get_compiler(using=using).execute_sql(return_id)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\sql\compiler.py" in execute_sql
      1288.             for sql, params in self.as_sql():

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\sql\compiler.py" in as_sql
      1241.                 for obj in self.query.objs

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\sql\compiler.py" in <listcomp>
      1241.                 for obj in self.query.objs

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\sql\compiler.py" in <listcomp>
      1240.                 [self.prepare_value(field, self.pre_save_val(field, obj)) for field in fields]

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\sql\compiler.py" in pre_save_val
      1192.         return field.pre_save(obj, add=True)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\fields\files.py" in pre_save
      288.             file.save(file.name, file.file, save=False)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\db\models\fields\files.py" in save
      87.         self.name = self.storage.save(name, content, max_length=self.field.max_length)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\files\storage.py" in save
      48.         name = self.get_available_name(name, max_length=max_length)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\files\storage.py" in get_available_name
      72.         while self.exists(name) or (max_length and len(name) > max_length):

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\files\storage.py" in exists
      308.         return os.path.exists(self.path(name))

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\core\files\storage.py" in path
      321.         return safe_join(self.location, name)

    File "C:\Users\Bryan\Envs\dj\lib\site-packages\django\utils\_os.py" in safe_join
      49.             'component ({})'.format(final_path, base_path))

    Exception Type: SuspiciousFileOperation at /admin/PortfolioApp/image/add/
    Exception Value: The joined path (C:\Users\Bryan\Documents\GitHub\Portfolio\media\pictures\VeselkaNYC-BJM.jpg) is located outside of the base path component (C:\Users\Bryan\Documents\GitHub\Portfolio\media\)

models.py

    class Image(models.Model):
        ...
        picture = models.ImageField(upload_to='pictures/', height_field='height', width_field='width', null=True)
        ...

settings.py

    ...
    STATIC_URL = '/static/'
    STATIC_ROOT = 'static/'

    MEDIA_URL = '/media/'
    MEDIA_ROOT = 'media/'
    ...

urls.py

    ...
    if settings.DEBUG:
        # Use static() to add url mapping to serve static files during development (only)
        urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
        urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

I've included what I think are the relevant code snippets but please ask if you need more to help me out. Any help is much appreciated.

EDIT: Thanks to Giancarlo's suggestions, redefined my STATIC_ROOT and MEDIA_ROOT as follows:

    STATIC_ROOT = os.join.path(BASE_DIR, "static")
    MEDIA_ROOT = os.join.path(BASE_DIR, "media")

I think the settings on my desktop work because of a .pyc cache file containing the correct settings that were saved during the development process. That's the best I can come up with.

bryan
  • 13
  • 1
  • 4

1 Answers1

2

Change your STATIC_ROOT and MEDIA_ROOT to

STATIC_ROOT = os.path.join(BASE_DIR, "static/")
MEDIA_ROOT = os.path.join(BASE_DIR, "media/")

Because maybe Windows is searching at C:\media or C:\static

Edit: I think BASE_DIR is pre-defined in settings.py, but here is if it is missing

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

And to be sure, add a trailing slash to the upload_to

upload_to='pictures/'
Giancarlo Ventura
  • 849
  • 1
  • 10
  • 27
  • I tried redefining the STATIC_ROOT and MEDIA_ROOT as you suggested but no luck. BASE_DIR is defined as you mentioned. And I noticed I was missing the trailing slash when I uploaded the question but fixing that didn't help. Still getting the exception. The trace back looks like the image is trying to be uploaded to the right folder. But something is wrong with how it is joining the static or media path with the base path. – bryan Dec 19 '18 at 22:24
  • I've got it working now. There must be a cache file with the correct paths on my desktop setup that makes it work. I used your definitions of STATIC_ROOT and MEDIA_ROOT but without the trailing slashes. Marking your answer as correct because it led me down the right path. Thanks! – bryan Dec 19 '18 at 22:36