How to server static files in the folder without folder 'static'? Django - django

I have my index.html and static files in this structure:
templates:
folder:
index.html
css
js
images
another_folder:
index.html
css
js
images
I want that staticfiles finder will look for static in templates by domain name in request. Please, without 'static' folder and {% load static %} tag. Thanks.

Okey, I am found a solution. Here is url for static: re_path(r'^(?P<path>.*)$', static, {'document_root': 'templates/lands/'}),
Here is my view:
def static(request, path, document_root=None, show_indexes=False):
domain = request.get_host()
path = domain + '/' + posixpath.normpath(path).lstrip('/')
fullpath = safe_join(document_root, path)
statobj = os.stat(fullpath)
content_type, encoding = mimetypes.guess_type(fullpath)
content_type = content_type or 'application/octet-stream'
response = FileResponse(open(fullpath, 'rb'), content_type=content_type)
response["Last-Modified"] = http_date(statobj.st_mtime)
if stat.S_ISREG(statobj.st_mode):
response["Content-Length"] = statobj.st_size
if encoding:
response["Content-Encoding"] = encoding
return response

Related

Django static files get from wrong url

I am migrating an old Django 2.2 project to Django 3.2.
I use AWS Cloudfront and S3.
I have done most of the tasks to do. But i have something weird in my static files serving.
When 'static' template tag is called, the rendered url is "https//<cloudfront_url>/static.." instead of "https://<cloudfront_url>/static..".
The comma disapears ! It obviously results to a 404 not found.
It worked without any problem on Django2.2.
So for the moment i dirty patched this by doing a '.replace("https//", "https://")' on django static template tag.
My settings relatives to static files are :
# settings.py
STATICFILES_LOCATION = 'static'
AWS_CLOUDFRONT_DOMAIN = <idcloudfront>.cloudfront.net
STATICFILES_STORAGE = 'app.custom_storages.StaticStorage'
STATIC_URL = "https://%s/%s/" % (AWS_CLOUDFRONT_DOMAIN, STATICFILES_LOCATION)
AWS_STORAGE_STATIC_BUCKET_NAME = 'assets'
# app.custom_storages.py
class StaticStorage(S3Boto3Storage):
location = settings.STATICFILES_LOCATION
bucket_name = settings.AWS_STORAGE_STATIC_BUCKET_NAME
def __init__(self, *args, **kwargs):
kwargs['custom_domain'] = settings.AWS_CLOUDFRONT_DOMAIN
super(StaticStorage, self).__init__(*args, **kwargs)
this seems to come from django storage since staticfiles_storage.url('css/a_css_file.css') return url without ":".
Oh ok after further investigations i see that come from storages.storages.backends.S3boto3.S3Boto3Storage from django-storages package.
https://github.com/jschneier/django-storages/blob/master/storages/backends/s3boto3.py#L577
url = '{}//{}/{}{}'.format(
self.url_protocol,
self.custom_domain,
filepath_to_uri(name),
'?{}'.format(urlencode(params)) if params else '',
)
":" seems to be missing.
Temporary fixed it by adding url_protocol = "https:" in my StaticStorage custom class and opened an issue on django-storage library.

Django - placing and accessing a js file in root directory

I am trying to implement Firebase cloud messaging push notifications in my Django project.
Firebase would look for a js file named firebase-messaging-sw.js in a project' root directory (no matter, what sort of project it is).
So, the problem is that I can't figure out what is my project's root directory (sorry, for being stupid), and how to make Firebase see this file. Just as an experiment, I copy-pasted the js file to each and every folder of my project, and still no success (Firebase service can't see the file).
Here's my settings.py file (with relevant content):
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ROOT_URLCONF = 'android_blend.urls'
WSGI_APPLICATION = 'android_blend.wsgi.application'
PROJECT_ROOT = os.path.abspath(os.path.dirname(__file__))
STATIC_URL = '/static/'
if DEBUG:
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(os.path.dirname(BASE_DIR),"static","static-only")
#STATIC_ROOT = [os.path.join(BASE_DIR,"static-only")]
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR),"static","media")
#MEDIA_ROOT = [os.path.join(BASE_DIR,"media")]
STATICFILES_DIRS = (
#os.path.join(os.path.dirname(BASE_DIR),"static","static"),
os.path.join(BASE_DIR,"static"),
)
My Django project layout is like this:
android_blend
android_blend
settings.py
url.py
wsgi.py
app1
app2
...
appN
manage.py
So the question is, what should I do in order for an outside service app (Google Firebase) be able to see the javascript file ?
In my browser, I get the following error:
A bad HTTP response code (404) was received when fetching the script.
I was also facing the same problem.
I tried it in the following way:
Add the following line in urls.py
url(r'^firebase-messaging-sw.js', views.firebase_messaging_sw_js),
Now add the function in view.py file
#csrf_exempt
def firebase_messaging_sw_js(request):
filename = '/static/firebase-messaging-sw.js'
jsfile = open(absAppPath + filename, 'rb')
response = HttpResponse(content=jsfile)
response['Content-Type'] = 'text/javascript'
response['Content-Disposition'] = 'attachment; filename="%s"' % (absAppPath + filename)
return response
So localhost:8000/firebase-messaging-sw.js will work properly...
Hope this will help you...

serving a directory of .html and .css in flask w/o specifying each file w/routes

how can i serve a directory of .html and .css in flask? my directory structure looks like the following:
./docs/ # contains html
./docs/_static # contains .css and images
i'd like to specify the file ./docs/index.html with the route:
#app.route('/docs/')
def documentation():
return render_template('docs/index.html')
and have all links to and from index.html that are contained within ./docs to be accessible without having to explicitly specify them with #app.route. any idea if/how this can be done?
thanks!
You have to put all your static files in a folder named static and all templates in a folder named templates. So your folder structure should look like this:
/docs/static/here_your_css_and_js_files
/docs/templates/here_your_html_files
To include your .css and .js files in your html your have to add in the head your html file:
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}">
The .html files are automatically founded by flask if you put them in the templates directory.
You can dynamically create your routes, or even create a custom view and walk through your docs directory and setup custom routes and template names. Something like this should work for you:
import os
from flask import Flask, render_template
from flask.views import View
HOME_BASE = os.path.dirname(os.path.abspath(__file__))
TEMPLATE_BASE = os.path.join(HOME_BASE, 'templates')
TEMPLATE_BASE_LEN = len(TEMPLATE_BASE)
class CustomTemplateView(View):
def __init__(self, template_name):
self.template_name = template_name
def dispatch_request(self):
return render_template(self.template_name)
app = Flask(__name__)
for root, dirs, files in os.walk(os.path.join(TEMPLATE_BASE, 'docs')):
for file in files:
if file.endswith('.html'):
template_name = os.path.join(root, file)[TEMPLATE_BASE_LEN:]
doc_path = os.path.splitext(template_name)[0]
app.add_url_rule(doc_path, view_func = CustomTemplateView.as_view(
doc_path.replace('/', '_'), template_name = template_name[1:]))
This code assumes that your docs directory layout looks like this:
yourproject/__init__.py
yourproject/templates/docs/403.html
yourproject/templates/docs/404.html
yourproject/templates/docs/accounts/add.html
yourproject/templates/docs/accounts/edit.html
yourproject/templates/docs/accounts/groups/template1.html
yourproject/templates/docs/accounts/index.html
yourproject/templates/docs/accounts/users/index.html
yourproject/templates/docs/accounts/users/template1.html
yourproject/templates/docs/accounts/users/template2.html
...
The code above should go into __init__.py file.

Django amazon s3 SuspiciousOperation

So when i try accessing a certain image on S3 from my browser everything works fine. But when python is doing it i get a SuspiciousOperation error.
My static folder is public on S3 so i really have no idea where this is coming from.
Publication.objects.get(id=4039).cover.url
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/home/vagrant/.pyenv/versions/blook/lib/python2.7/site-packages/django/db/models/fields/files.py", line 64, in _get_url
return self.storage.url(self.name)
File "/home/vagrant/.pyenv/versions/blook/lib/python2.7/site-packages/queued_storage/backends.py", line 291, in url
return self.get_storage(name).url(name)
File "/home/vagrant/.pyenv/versions/blook/lib/python2.7/site-packages/queued_storage/backends.py", line 115, in get_storage
elif cache_result is None and self.remote.exists(name):
File "/home/vagrant/.pyenv/versions/blook/lib/python2.7/site-packages/storages/backends/s3boto.py", line 410, in exists
name = self._normalize_name(self._clean_name(name))
File "/home/vagrant/.pyenv/versions/blook/lib/python2.7/site-packages/storages/backends/s3boto.py", line 341, in _normalize_name
name)
SuspiciousOperation: Attempted access to 'http:/s3-eu-west-1.amazonaws.com/xpto/static/images/default-image.png' denied.
My settings:
AWS_S3_SECURE_URLS = True # use http instead of https
S3_URL = 'http://s3-eu-west-1.amazonaws.com/%s' % AWS_STORAGE_BUCKET_NAME
MEDIA_ROOT = 'media/'
STATIC_ROOT = '/static/'
STATIC_URL = S3_URL + STATIC_ROOT
MEDIA_URL = S3_URL + '/' + MEDIA_ROOT
For now i can work around it, but that is not a long term solution. any ideas?
Danigosa's answer in this thread is the answer:
django-storages and amazon s3 - suspiciousoperation
Create a special storage class as outlined in this post:
Using Amazon S3 to store your Django sites static and media files.
Then override _normalize_name like this:
from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage
class StaticStorage(S3Boto3Storage):
location = settings.STATICFILES_LOCATION
def _clean_name(self, name):
return name
def _normalize_name(self, name):
if not name.endswith('/'):
name += "/"
name = self.location + name
return name
class MediaStorage(S3Boto3Storage):
location = settings.MEDIAFILES_LOCATION
def _clean_name(self, name):
return name
def _normalize_name(self, name):
if not name.endswith('/'):
name += "/"
name = self.location + name
return name
Finally - (on Python 3 at least) DON'T use
{% load static from staticfiles %}
in your templates.
Stick with:
{% load static %}
I recently faced the same issue. Tried all the available solutions. Realised that the issue was with the '/' that I had added while calling static files. Like this, "{% static '/path/to/static/file' %}", changed this to "{% static 'path/to/static/file' %}"

Failing to show images in templates Django

I have problems showing images in my Django templates (I'm uploading the images from the admin application). I read the documentation and other posts about the upload_to and still couldn't figure it out. I tried this <img src="{{ a.image}}"/> in my template and then this <img src="{{MEDIA_URL}}{{ a.image}}"/> and same results. Here is my settings.py code :
MEDIA_ROOT = '/home/mohamed/code/eclipse workspace/skempi0/media'
MEDIA_URL = '/media/'
and finally, I tried the following in my models.py and I failed miserably:
image = models.ImageField(upload_to = "ads/")
image = models.ImageField(upload_to = ".")
and when I used image = models.ImageField(upload_to = MEDIA_URL) I got the following error
SuspiciousOperation at /admin/advertisments/banner/add/
Attempted access to '/media/cut.jpg' denied.
EDIT
Generated links are as follows :
<img src="./DSCN6671.JPG">
RE-EDIT
Here is my view:
def index(request):
spotlightAlbum = Album.objects.filter(spotlight = True)
spotlightSong = Song.objects.filter(spotlight = True).order_by('numberOfPlays')
homepage = Song.objects.filter(homepage = True).order_by('numberOfPlays')
ads = Banner.objects.all()
copyright = CopyrightPage.objects.get()
try:
user = User.objects.get(userSlug = "mohamed-turki")
playlists = UserPlaylist.objects.filter(owner = user.userFacebookId)
purchase = Purchase.objects.filter(userName = user.userFacebookId)
user.loginState = 1
user.save()
except:
user = None
playlists = None
context = {'copyright':copyright, 'ads':ads, 'spotlightSong':spotlightSong,'spotlightAlbum': spotlightAlbum, 'homepage':homepage, 'user':user, 'playlists':playlists, 'purchase':purchase }
return render_to_response('index.html',context,context_instance = RequestContext(request))
Could anybody tell me what am I doing wrong??
P.S I'm using Django 1.4
The path you provide in upload_to will be a relative path from the MEDIA_ROOT you set in your project's settings file (typically settings.py).
Your MEDIA_ROOT is where your uploaded media will be stored on disk while the MEDIA_URL is the URL from which Django will serve them.
So if your MEDIA_ROOT is /home/mohamed/code/eclipse workspace/skempi0/media and your model's image attribute is:
image = models.ImageField(upload_to = "ads/")
Then the final home on disk of your uploaded image will be /home/mohamed/code/eclipse workspace/skempi0/media/ads/whatever-you-named-your-file.ext and the URL it will be served from is /media/ads/whatever-you-named-your-file.ext
Setting your upload path to be settings.MEDIA_URL won't work because that's where the media is served FROM not where it is allowed to be stored on disk.
If you want to load your uploaded image in your templates just do this (replace whatever with the name of the variable sent from the view to the template that represents this object):
<img src="{{ whatever.image.url }}"/>
The image attribute on your model isn't actually an image, it's a Python class that represents an image. One of the methods on that ImageField class is .url() which constructs the path to the URL of the image taking into account how you set your MEDIA_URL in your project's settings. So the snippet above will generate HTML like this:
<img src="/media/ads/whatever-you-named-your-file.ext"/>
RequestContext() and settings.TEMPLATE_CONTEXT_PROCESSORS
Since the render_to_response() you are returning from your view is utilizing RequestContext() you need to make sure you have settings.TEMPLATE_CONTEXT_PROCESSORS set correctly. Check out the 1.4 docs for further clarification.
upload_to needs to be an absolute path to the directory, not the web url. So try this:
image = models.ImageField(upload_to = settings.MEDIA_ROOT)
in your templates, just use
<img src="{{ a.image.url }}">
First, I suggest to change the MEDIA_ROOT to be
MEDIA_ROOT = os.path.join(PROJECT_ROOT,'media')
this way you ensure the media directory path is right under your project root. Your MEDIA_URL is set up correctly, but it is not used when uploading a file. MEDIA_ROOT is.
Regarding the file upload, in your model set the field to be
image_field = models.ImageField('Image', upload_to='images/some_sub_folder')
Note that I didn't use neither a leading nor trailing forward slashes. The above will upload the image to PROJECT_ROOT/media/images/some_sub_folder/ with the filename and extension of the original file. Alternatively, you can alter the filename by using a callable - upload_to=filename_convetion - more info here.
In your template, you can access the image using
<img src="/media/{{ your_model.image_field }}" />
Hope this helps, cheers.
I know this question is old but I had the same problem and this solved in my case:
settings.py
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
PROJECT_DIR = os.path.dirname(__file__)
MEDIA_ROOT = os.path.join(PROJECT_DIR, "media")
MEDIA_URL = '/media/'
urls.py
Then, my urls.py was missing this line of code to discover the /media/ folder and show the content:
urlpatterns += staticfiles_urlpatterns()
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}, name="media_url"),
) + urlpatterns
Hope it can help someone.