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.
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\)
class Image(models.Model):
...
picture = models.ImageField(upload_to='pictures/', height_field='height', width_field='width', null=True)
...
...
STATIC_URL = '/static/'
STATIC_ROOT = 'static/'
MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'
...
...
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.
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/'