Let me start by saying I have spent a lot of energy on this and any input is very valuable to me at this point. Now, onto my question:
What I have working: My website provides a simple interface for users to upload a file in which they will be given a key. The file is saved to the server and the key is generated using a modern, secure encryption algorithm. These details are not important to the question at hand.
What I want to implement: On a download page, the user can enter the key and the file will be downloaded to their computer. Want I want to know is how to provide a user with this ability to download an uploaded file. To put it simply, I just want to allow users to download a file in the MEDIA_ROOT folder.
I am using class-based views, so my urls are:
urls.py
from django.conf.urls import patterns, include, url
from django.conf.urls.static import static
from django.conf import settings
from fileupload.views import FileDownloadView, GetFileView
urlpatterns = patterns('',
url(r'^key/?$', FileDownloadView.as_view(), name='getkey'),
url(r'^download/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}, GetFileView.as_view()),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Please provide me with the basic idea on what functions I should use in my views to actually download the file. I am still in development so I want to take advantage of Django's ability to serve files, not Apache's. I will consider other options if I go into production. Whatever code I need to post, I can post.
Thank you!
If the key grants them access to the file, why not have the key be the file name? That is, when the file is uploaded, create a long key (say 64 characters long) and rename the file to the key name. Then simply serve up that file using the static.serve method when it is requested.
The benefits of this approach are: 1) You don't need to map the key to the file in the database, and 2) you don't need to validate the key when retrieving the file. If the file name is found, the key is valid.
Basically I told you, Django should not serve files in production.
After is it possible yes:
You can serve a file tree with django.conf.urls.static.static(), as a view in your urls.py. Example:
urlpatterns = patterns('',
...
static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
...
)
Or use whitenoise, a package for server static file with Python web apps. It is very simple to use, only change your wsgi.py file like this:
from django.core.wsgi import get_wsgi_application
from whitenoise.django import DjangoWhiteNoise
application = get_wsgi_application()
application = DjangoWhiteNoise(application)
And use 'whitenoise.django.GzipManifestStaticFilesStorage' as STATICFILES_STORAGE.
Along with the url file I have shown in the original question, all I needed to do from here was to provide an HttpResponseRedirect('/path/of/my/media/file')
Very simple!
Related
I am going to build a Django storage application where users can upload files to the server. Users also can download their files from the server. To download the file, the server returns the URL of the media file to the user. I added a permission class, if the user is authenticated & owner of the file then return the URL of the media file, otherwise return 404. But there is a problem, if any unauthenticated user found the URL of the media file from the history of the browser, then he can download/access the file. How can I handle this case?
The problem you're mentioning is not related to your backend application. It's related to the web server serving your media files (e.g. Nginx). In order to handle this problem, you should generate secure links from your django app which are bound to specific user IPs and have an expiration time.
You can read this article for more info:
https://www.nginx.com/blog/securing-urls-secure-link-module-nginx-plus/
Django's development server does not deal with static/media files
please take a look at this django-sendfile
I personally liked what Michal Májský suggested in his blog here:
from django.conf.urls import patterns, include, url
from django.contrib.auth.decorators import login_required
from django.views.static import serve
from django.conf import settings
#login_required
def protected_serve(request, path, document_root=None, show_indexes=False):
return serve(request, path, document_root, show_indexes)
urlpatterns = patterns('',
url(r'^%s(?P<path>.*)$' % settings.MEDIA_URL[1:], protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
This is just verifies that any request to the media folder will go through authentication check, right from the urls.py
My goal is to have an Angular project being served from the root of my development server. The files will be completely static as far as Django is concerned, no Django template processing is needed. The angular project will then make resource calls to a Django project located at /api/ on the same development server, which will then return json results generated from a view for the Angular project to process.
I assumed it would be as easy as adding the following to my urls.py file.
url(r'^/', 'django.views.static.serve', {
'document_root':'/Users/kyle/Development/project/site/app',
}),
Or
+ static("^/$", document_root="/Users/kyle/Development/project/site/app")
To the end of the urlpatterns.
With /project/site/app being the directory with the Angularjs files.
However, both of these leave me with 404 errors.
I'm open to changing the structure of the project if a more obvious solution exists.
You need to serve both index.html and your static files on / which is done like this in Django 1.10:
from django.contrib.staticfiles.views import serve
from django.views.generic import RedirectView
urlpatterns = [
# / routes to index.html
url(r'^$', serve,
kwargs={'path': 'index.html'}),
# static files (*.css, *.js, *.jpg etc.) served on /
url(r'^(?!/static/.*)(?P<path>.*\..*)$',
RedirectView.as_view(url='/static/%(path)s')),
]
See this answer where I wrote a more complete explanation of such a configuration – especially if you want to use it for production.
It turned out that it was a combination of 2 things, as shavenwarthog said, it shouldn't have the slash. Also, it needed a regular expression to direct it to the file. The final line ended up being:
url(r'^(?P<path>.*)$', 'django.views.static.serve', {
'document_root':'/Users/kyle/Development/project/site/app',
}),
I can then access files like
http://localhost/beer.jpg
note that by default Django won't serve a directory listing. Do you still get a 404 if file /Users/kyle/Development/project/site/app/beer.jpg doesn't appear as http://localhost/beer.jpg ?
in urls.py URLs don't start with a slash; compare url(r'beer') with url(r'^/beer')
I suggest just going for the standard STATIC support. It's awkward, but lets you serve file simply during development, and switch to a 3rd party server (ie Nginx) for production:
https://docs.djangoproject.com/en/dev/howto/static-files/
Here is my current method of serving robots.txt
url(r'^robots\.txt/$', TemplateView.as_view(template_name='robots.txt',
content_type='text/plain')),
I don't think that this is the best way. I think it would be better if it were just a pure
static resource and served statically. But the way my django app is structured is that the static root and all subsequent static files are located in
http://my.domain.com/static/stuff-here
Any thoughts? I'm amateur at django but
TemplateView.as_view(template_name='robots.txt',
content_type='text/plain')
looks a lot more resource consuming than just a static call to my static directory which is served on nginx.
Yes, robots.txt should not be served by Django if the file is static. Try something like this in your Nginx config file:
location /robots.txt {
alias /path/to/static/robots.txt;
}
See here for more info: https://nginx.org/en/docs/http/ngx_http_core_module.html#alias
Same thing applies to the favicon.ico file if you have one.
The equivalent code for Apache config is:
Alias /robots.txt /path/to/static/robots.txt
I know this is a late reply, I was looking for similar solution when don't have access to the web server config. So for anyone else looking for a similar solution, I found this page: http://www.techstricks.com/adding-robots-txt-to-your-django-project/
which suggests adding this to your project url.py:
from django.conf.urls import url
from django.http import HttpResponse
urlpatterns = [
#.... your project urls
url(r'^robots.txt', lambda x: HttpResponse("User-Agent: *\nDisallow:", content_type="text/plain"), name="robots_file"),
]
which I think should be slightly more efficient that using a template file, although it could make your url rules untidy if need multiple 'Disallow:' options.
Is there a way to split the url mappings in multiple files in Google App Engine?
I want something like this:
from app1.controller import App1Handler
from app2.controller import App2Handler
app = webapp2.WSGIApplication([(r'/app1', App1Handler),(r'/app1', App2Handler)])
In App1Handler, I would like to specify some thing like this:
(r'/action1', Action1Handler), (r'/action2', Action2Handler)
In summary, when user access /app1/action1, Action1Handler has to be executed.
Django has a similar feature, where admin site urls are included in the main url patterns.
urlpatterns = patterns('',
url(r'^polls/$', 'polls.views.index'),
url(r'^admin/', include(admin.site.urls)),
)
Is there any such provisions available in GAE?
You can split between the 2 files at the app.yaml level:
- url: /app1/.*
script: file1.py
- url: /app2/.*
script: file2.py
I think you will still need to add /app1 in all your urls in the file1.py files e.g.
app = webapp2.WSGIApplication([(r'/app1/myview', Handler),(r'/app1/myotherview', AnotherHandler)])
I think this is better because you can use handlers with the same name in the 2 modules, whereas you would get a conflict if you imported 2 handlers with the same name in a main file.
You could do what #user375348 described by using app.yaml, otherwise you'd need to build your own. There's an simple router in the webapp2 documentation you can pretty much use straight up.
http://webapp-improved.appspot.com/guide/handlers.html
I'm new to Django and working my way through "The Django Book" by Holovaty and Kaplan-Moss. I have a project called "mysite" that contains two applications called "books" and "contact." Each has its own view.py file. In my urls.py file I have the following:
from books import views
from contact import views
...
urlpatterns = patterns('',
...
(r'^search/$', views.search),
(r'^contact/$', views.contact),
...
When I run my code I get this error:
NameError at /search/
...
Exception value: 'module' object has no attribute 'search'
What I believe is happening is that since views from contact was imported last, Django is looking at contact's view which does not contain search (search is in books' view).
What is the proper way to import the views.py file from two distinct applications within a Django urls file?
Thanks for your help!
Disclaimer: Not a Django answer
The problem is with these two lines:
from books import views
from contact import views
The second import is shadowing the first one, so when you use views later you're only using the views from contact.
One solution might be to just:
import books
import contact
urlpatterns = patterns('',
...
(r'^search/$', books.views.search),
(r'^contact/$', contact.views.contact),
...
I'm not sure, but I also think that you don't actually need to import anything and can just use strings in your pattern, something like: 'books.views.search'.
Another possiblity is to follow Simon Visser suggestion:
from books.views import search
from contact.views import contact
from books import views
from contact import views
You are overwriting the name views. You need to import them as different names or as absolute names.
import books.views
import contact.views
... or ...
from books import views as books_views
from contact import views as contact_views
Then use the correct name when defining your URLs. (books.views.search or books_views.search depending on the method you choose)
The reason I’m answering this question is because it was answered years ago and those answers are not correct or useful anymore for newer Django versions, or there is a better practice you should know about.
So, if you have more than one app in your Django project then you should use a new urls.py file for every one of your apps. It means that if you start a new app then you have to manually create a new file called urls.py in the subfolder of your new app. Many beginners first do not understand why this is good, but this is a good practice if you plan creating more apps in one Django project.
When you start a project, the urls.py file automatically created in your project folder, but if you create/start a new app in Django, then it is a good practice if you create a separate urls.py for that app in its own folder. (And that way you will never have the "importing different app's views into urls.py" problem in the first place).
After you created the urls.py file for your app, then you have to include that app’s urls.py file in your project’s urls.py file in the following way:
Let’s see an example when you create a new app called ‘my_new_app’.
This is how your project’s main urls.py file should look like:
from django.conf.urls import url, include
from django.contrib import admin
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^my_new_app/', include('my_new_app.urls')),
]
In your project’s urls.py file you have to import the ‘include’ method, then you can include your my_new_app urls.py file in your project’s main urls.py file. In your my_new_app folder you have to manually create a urls.py file as I stated above. Then you have to use that file for all of your urlpatterns of your my_new_app. Then of course this way it’s going to be automatically included in your project’s main urls.py file.
So this is then how your my_new_app own urls.py file should look like:
from django.conf.urls import url
from my_new_app import views
urlpatterns = [
url(r'^$', views.index, name = "index"),
]
Assuming that you also created a first view called ‘index’ in your ‘my_new_app/views.py file.
my_new_app/views.py file look like this:
from django.shortcuts import render
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello World!")
And you can check your my_new_app in your browser at:
http://localhost:8000/my_new_app
(Of course you can give any url to your my_new_app in your project's urls.py file.)
Now, you can create another app, in your Django project, called my_second_app and you should repeat the above steps for that app too. This way you will not have any problem importing views from different apps into urls.py files. This would be a very basic “good practice solution” for this problem in 2017 in Django 1.11.
The URLconfs documentation gives an example of the same situation
You can skip the imports and separate the urls by app as such:
urlpatterns = patterns('books.views',
(r'^/book/search/$', 'search'), #calls books.views.search
)
urlpatterns += patterns('contact.views', #make note of the '+='
(r'^/contact/search/$', 'search'), #calls contact.views.search
)
Heres the approach i took for different view/API versions:
from django.urls import path
from my_app import views as api_v1
from my_app import views_v2 as api_v2
urlpatterns = [
path('view_one', api_v1.ViewOne.as_view()),
path('view_two', api_v2.ViewTwo.as_view()),
]