I have a small Django app, and I'm trying to access data from a CSV file (static/blog/dat.csv, the static folder is at the same level as the templates folder and views.py; and everything is inside my blog app) so I can use it to plot a graph on the browser using Chart.js. Aside from not being able to do that, the app is working fine.
I know I'm gonna need to pass some sort of context to the view function, but I don't know how I'd do that. Also, I have a couple of similar csv files, and using them as static files in my app seems simpler and easier than to add everything to a database to access them that way.
# views.py
from django.shortcuts import render
from django.contrib.staticfiles.storage import staticfiles_storage
import csv
def rtest(request):
url = staticfiles_storage.url('blog/dat.csv')
with open(url, 'r') as csv_file:
csv_reader = csv.reader(csv_file)
for line in csv_reader:
context += line
return render(request, 'blog/r.html', context)
# urls.py
urlpatterns = [
# ...
path('r-test/', views.rtest, name='blog-r-test'),
]
Here is the error I'm getting:
FileNotFoundError at /r-test/
[Errno 2] No such file or directory: '/static/blog/dat.csv'
I'm sure this is not the only error.
I know that the way I'm using the context variable is wrong, but that's just to kind of show what I'm trying to do. If I could just print one cell from the csv, I would view this as a win. Please help, thank you!
------Edit1-------
After using staticfiles_storage.path() instead of staticfiles_storage.url()
ImproperlyConfigured at /r-test/
You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path.
------Edit2------
I'm now able to find my csv file:
STATIC_URL = '/static/'
STATIC_ROOT = 'C:/Users/riccl/Documents/richie/Python/nuclear/main/book/static/book'
But my context variable still doesn't make any sense.
You need to use staticfiles_storage.path() to read the file. staticfiles_storage.url() will return the URL that a user will use to load the static file on your site
STATIC_ROOT is where all static files will be stored after you run collectstatic, most of the time this is set to <root of the project>/static/. This is also where staticfiles_storage.path() will look for static files.
You will also need to set STATICFILES_DIRS so that your file(s) can be found by collectstatic. I usually have a folder located at <root of the project>/<project name>/static/ which I add to STATICFILES_DIRS
Related
Django 1.9.7
Could you help me cope with user uploaded images. I have managed to save images.
But I can't show them. So far this is all about development stage (not production).
The bottommost code sample shows the html when I execute "View page source" in Chrome. This "src="/home/michael/workspace/..." is absolute path. It will work if I create such html and open it in the browser without a webserver.
But whey I run the Django dev server, the image doesn't show.
Could you give me a kick here.
/pharchive/pharchive/settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, '../media/')
MEDIA_URL = os.path.join(BASE_DIR, '../media/')
/pharchive/pharchive/urls.py
from django.conf.urls.static import static
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
/pharchive/masterdocument/models.py
class Image(AbstractDocument):
image = models.ImageField(upload_to='images/%Y/%m/%d')
/pharchive/masterdocument/views.py
class ImageDetailView(DetailView):
model = Image
/pharchive/masterdocument/templates/masterdocument/image_detail.html
<html>
<img src="{{ object.image.url }}"/>
</html>
view-source:http://localhost:8000/images/6/
<html>
<img src="/home/michael/workspace/pharchive/media/images/2016/06/29/Screenshot_from_2016-02-23_205205.png"/>
</html>
MEDIA_URL should be the root URL for uploaded media, for example:
MEDIA_URL = '/media/'
You have set it to the path the the folder where uploaded media are copied.
Edit: as requested more information on how that works.
What you are trying to do is store an image file submitted by a user, and then serve it to other users. To store the file, you need to specify a location on the filesystem where to store it, in your case:
/home/michael/workspace/pharchive/media/images/2016/06/29/Screenshot_from_2016-02-23_205205.png
Django builds this path by concatenating these parts:
the MEDIA_ROOT
the ImageField's interpolated upload_to attribute, here 'images/%Y/%m/%d' interpolated to images/2016/06/29
the file name, here Screenshot_from_2016-02-23_205205.png.
Django also stores in the database the path to the file, relative to the MEDIA_ROOT, in your case images/2016/06/29/Screenshot_from_2016-02-23_205205.png
Now, to serve the stored file to your users, it first needs to be accessible through a URL, this URL is built by concatenating the MEDIA_URL setting to the path stored in the database (maybe modified to make it URL compatible), here it gives /media/images/2016/06/29/Screenshot_from_2016-02-23_205205.png.
The last step is to actually serve the file when the previously constructed URL is accessed, typically it will not be done the same way in development and in production.
In development, the Django devserver will be used to serve the file, which is why you add a url pattern when DEBUG is true. That url pattern will map any URL starting with the MEDIA_URL (/media/) to a view that will read the stored file and return its content.
In production you will use a dedicated web server to serve uploaded files, for performance reasons.
To integrate Django and Ember, I have decided to serve my Ember SPA in a Django view (avoids CORS issues, only one server for frontend and API, etc). I do it like this:
# urls.py
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^api/', include(api_urls, namespace='api')),
...
url(r'^$', views.emberapp, name='emberapp'),
...
]
# views.py
from django.http import HttpResponse
def emberapp(request):
# The Ember frontend SPA index file
# This only works in development, and is anyway hacky
EMBER_FE_INDEX_HTML = '/absolute/path/to/my/frontend/static/fe-dist/index.html'
template_file = open(EMBER_FE_INDEX_HTML)
html_content = index_file.read()
index_file.close()
return HttpResponse(html_content)
The index.html is part of the static assets. In development this is very easy:
The index.html is directly accessible to the Django application in the file system
I know the absolute path to the index file
But in production things are more complex, because the static assets are not local to the django application, but accessible on Amazon S3. I use django-storages for that.
How can I read the contents of a static file from a view, in a generic way, no matter what backend is used to store/serve the static files?
First, I don't think the way you do it is a good idea.
But, to answer your question: In your settings.py, you likely have defined the directory where Django will collect all static files.
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
So in your view, you just need to fetch the file os.path.join(settings.STATIC_ROOT, 'index.html')
That said, you should serve index.html via the webserver, same as your static/ files, robots.txt, favicon.ico, etc. Not through Django. The webserver is much faster, uses proper caching, and its just one line in your Nginx or Apache settings, instead of an entire view function in Django.
This is my current solution. Works in development, not sure about production yet (it is a pain that you need to commit untested code to verify production-related code in Heroku)
from django.conf import settings
from django.http import HttpResponse
from django.core.files.storage import get_storage_class
FE_INDEX_HTML = 'fe/index.html' # relative to the collectstatic directory
def emberapp(request):
# The Ember frontend SPA index file
# By getting the storage_class like this, we guarantee that this will work
# no matter what backend is used for serving static files
# Which means, this will work both in development and production
# Make sure to run collectstatic (even in development)
# TODO: how to use this in development without being forced to run collectstatic?
storage_class = get_storage_class(settings.STATICFILES_STORAGE)
# TODO: reading from a storage backend can be slow if assets are in a third-party server (like Amazon S3)
# Maybe streaming the static file from the server would be faster?
# No redirect to the Amazon S3 asset, please, since the Ember App needs to
# run from the same URL as the API, otherwise you get CORS issues
with storage_class().open(FE_VWORKS_INDEX_HTML) as index_file:
html_content = index_file.read()
return HttpResponse(html_content)
Or, to reply with an StreamingHttpResponse, which does not force Django to read the whole file in memory (and wait for it to be read):
def emberapp(request):
# The Ember frontend SPA index file
# By getting the storage_class like this, we guarantee that this will work
# no matter what backend is used for serving static files
# Which means, this will work both in development and production
# Make sure to run collectstatic (even in development)
# TODO: how to use this in development without being forced to run collectstatic?
storage_class = get_storage_class(settings.STATICFILES_STORAGE)
index_file = storage_class().open(FE_INDEX_HTML)
return StreamingHttpResponse(index_file)
in my models.py I have (myfile.txt is 3gb)
with open('/static/myfile.txt', 'rb') as f:
do something..
but it complains
No such file or directory: '/static/myfile.txt'
this is my settings.py
STATIC_ROOT = 'absolute_dir_to_project/app_name/static/'
STATIC_URL = '/static/'
Did I miss any step?
you should prepend the absolute path with SETTINGS's STATIC_URL so that it looks for myfile.txt inside Django:
from django.conf import settings # dont forget to import this guy
with open(settings.STATIC_URL + 'myfile.txt', 'rb') as f:
do something..
The open function is trying to open a file from disk with the path /static/myfile.txt, rather than requesting a file through your Django web app. It doesn't sound like you need to have this file in your static files folder, especially if it's huge and isn't intended for public consumption
how can I get the link of a FileField? I tried the url field, but it gives the file path:
In [7]: myobject.myfilefield.path
Out[7]: u'/home/xxxx/media/files/reference/1342188147.zip'
In [8]: myobject.myfilefield.url
Out[8]: u'/home/xxxx/media/files/reference/1342188147.zip'
I was expecting to get http://<mydomain>/media/files/reference/1342188147.zip
How can I get that? Do I have to build the string manually?
EDIT
My MEDIA_URL was not set, but I still can't get it to work:
settings.py
MEDIA_ROOT = '/home/xxx/media/'
MEDIA_URL = 'http://xxx.com/media/'
models.py
class Archive(models.Model):
#...
file = models.FileField(upload_to="files")
in shell
a = Archive()
a.file = "/some/path/to/file.txt"
a.save()
Then I get for a.path:
"/some/path/to/file.txt"
and for a.url:
"http://xxx.com/media/some/path/to/file.txt"
When done programmatically, a.file = "/some/path/to/file.txt", the file is not uploaded to MEDIA_ROOT. How can I upload a file in the directory defined by upload_to, i.e. in my case /home/xxx/media/file.txt?
The output is based on your settings file, have a look here for an understanding on serving staticfiles in development and/or production:
Confusion in Django admin, static and media files
I'm guessing you have the field defined as:
picture = models.ImageField(upload_to="/home/xxx/media/files/reference")
in other words is it possible you have defined an absolute path for the upload_path property of the field ?
Try something like
from django.conf import settings
picture = models.ImageField(upload_to=settings.MEDIA_ROOT + "files/reference")
I'm using reportlab pdfgen to create a PDF. In the PDF there is an image created by drawImage. For this I either need the URL to an image or the path to an image in the view. I managed to build the URL but how would I get the local path to the image?
How I get the URL:
prefix = 'https://' if request.is_secure() else 'http://'
image_url = prefix + request.get_host() + STATIC_URL + "images/logo_80.png"
# Older Django <3.0 (also deprecated in 2.0):
from django.contrib.staticfiles.templatetags.staticfiles import static
# Django 3.0+
from django.templatetags.static import static
url = static('x.jpg')
url now contains '/static/x.jpg', assuming a static path of '/static/'
EDIT: If you're on Django >=3.0, refer to Django get the static files URL in view instead. This was answered with Django 1.X version.
dyve's answer is good one, however, if you're using "cached storage" on your django project and final url paths of the static files should get "hashed"(such as style.aaddd9d8d8d7.css from style.css), then you can't get a precise url with django.templatetags.static.static(). Instead, you must use template tag from django.contrib.staticfiles to get hashed url.
Additionally, in case of using development server, this template tag method returns non-hashed url, so you can use this code regardless of that the host it is development or production! :)
from django.contrib.staticfiles.templatetags.staticfiles import static
# 'css/style.css' file should exist in static path. otherwise, error will occur
url = static('css/style.css')
From Django 3.0 you should use from django.templatetags.static import static:
from django.templatetags.static import static
...
img_url = static('images/logo_80.png')
here's another way! (tested on Django 1.6)
from django.contrib.staticfiles.storage import staticfiles_storage
staticfiles_storage.url(path)
Use the default static tag:
from django.templatetags.static import static
static('favicon.ico')
There is another tag in django.contrib.staticfiles.templatetags.staticfiles (as in the accepted answer), but it is deprecated in Django 2.0+.
#dyve's answer didn't work for me in the development server. Instead I solved it with find. Here is the function:
from django.conf import settings
from django.contrib.staticfiles.finders import find
from django.templatetags.static import static
def get_static(path):
if settings.DEBUG:
return find(path)
else:
return static(path)
If you want to get absolute url(including protocol,host and port), you can use request.build_absolute_uri function shown as below:
from django.contrib.staticfiles.storage import staticfiles_storage
self.request.build_absolute_uri(staticfiles_storage.url('my-static-image.png'))
# 'http://localhost:8000/static/my-static-image.png'
In short words you need to get
STATIC_URL
STATIC_ROOT
urlpatterns
staticfiles
templatetags
url parameters
All in the right place, to get this working. In addition, in real time deployment, circumstances vary, which it is very possible that the current setting you spent 3 hours worked out, works on your local machine but the server.
So I adopted the traditional way!!
app
├── static
│ └── json
│ └── data.json
└── views.py
views.py
import os
with open(os.path.abspath(os.getcwd()) + '/app/static/json/data.json', 'r') as f:
pass