Why does Django only serve files containing a space? - python

I'm writing a basic Django application. For testing / development purposes I'm trying to serve the static content of the website using Django's development server as per http://docs.djangoproject.com/en/dev/howto/static-files/#howto-static-files.
My urls.py contains:
(r'^admin/(.*)', admin.site.root),
(r'^(?P<page_name>\S*)$', 'Blah.content.views.index'),
(r'^static/(?P<path>.*)$', 'django.views.static.serve',
{'document_root': 'C:/Users/My User/workspace/Blah/media',
'show_indexes': True})
However, when I try to access a file such as http://localhost:8000/static/images/Logo.jpg Django gives me a 404 error claiming that "No Page matches the given query."
When I try to access a file such as http://localhost:8000/static/images/Blah%20Logo.jpg it serves the file!
What's going on?

You have wrong patterns order in urls.py.
When you try to retrieve path without space it matches:
(r'^(?P<page_name>\S*)$', 'Blah.content.views.index'),
not static.serve and of course you have not such page, But when you try to access path with space it matches proper static.serve pattern because it is more generic and allows spaces.
To solve this problem just swap those patterns.

Related

Django - Protecting Media files served with Apache with custom login_required decorator

I deployed a Django app using Apache, and I check for authentication in most views using a decorator.
#custom_decorator
def myView(request):
bla bla bla...
It's not the #login_required decorator that comes with Django, but it's almost the same thing, except that allows access only to users from certain groups. This works as intended.
Also, I'm serving media (user uploaded) files with Apache, like this:
Alias /media /path/to/media
<Directory /path/to/media>
Require all granted
</Directory
I can access the media files just fine, but the problem is that I can access them even if I'm not logged in, simply by typing the url manually, like:
mySite/media/myFile.png
Is there a way to limit access to the media files, hopefully using the custom decorator?
I stumbled across a similar question: How do you Require Login for Media Files in Django, but unfortunately the answer went way over my head.
Thanks in advance!
When you mention media path to the apache, those files are served directly by Apache (or Nginx or any other web server). Those requests do not even goes through your Django application. Hence you do not have a control over those requests or the data served by them.
One way is to create your separate API to serve the static/media files. In that API, use the same validation that you do for other content.
Even better, if you have AWS (Amazon Web Services) or GCP (Google Cloud Platform) account, store the static files on the S3 or Cloud Storage respectively and serve their URL of files via your API.
PS: Do not forget to remove the media path from the Apache configuration. Else, Apache will keeps on serving those file.
Alternatively, as mentioned in Sarafeim's answer to Restricting access to private file downloads in Django which requires modification in both sever and application side. You need a way for your HTTP server to ask the application server if it is ok to serve a file to a specific user requesting it. You may achieve this using django-sendfile which uses the X-SendFile mechanism. As per the django-sendfile's README:
This is a wrapper around web-server specific methods for sending files to web clients. This is useful when Django needs to check permissions associated files, but does not want to serve the actual bytes of the file itself. i.e. as serving large files is not what Django is made for.
To understand more about the sendfile mechanism, read: Django - Understanding X-Sendfile
Okay, so based on #MoinuddinQuadri answer and links, it seems that the easiest solution is to serve the files using a regular Django view, and apply the desired decorator, like this:
#custom_decorator
viewFile(request, objectID):
object = MyModel.object.get(id = objectID)
return HttpResponse(object.file, content_type = "image/png")
(In my case, I wanted to serve a FileField related to a Model, so in the view I pass the ID of the object instead of the file name).
Also, I commented out the corresponding code in the Apache conf:
### Alias /media /path/to/media
### <Directory /path/to/media>
### Require all granted
###</Directory
I had to change some templates to use the new view instead of the URL of the media file, but now it works as intended, locking out non-logged users.
However, this no longer uses Apache to serve the files, it uses Django itself, which according to the docs, is inneficient and not recommended.
Ideally you want to still serve the files using Apache and just use the view to protect its access, and for that you can use mod_xsendfile for Apache, or simply use Django Sendfile, which is a wrapper for the module just mentioned.
I tried the latter, but unfortunately it has problems with file names that have non-ascii characters. As my target are spanish-speaking users, I had to resort of just serving the files with Django, at least for now.
I used solution #1 in the post "Django protected media files". There are two other solutions described here as well: "Unpredictable Urls" and "X-Sendfile", but the one I'm describing was my choice.
As #Sauvent mentioned, this causes the files to be served by Django and not by a web server (e.g. Apache). But it's quick and easy if you're not dealing with a lot of traffic or large files.
Basically, add the following to your urls.py:
#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'^{}(?P<path>.*)$'.format(settings.MEDIA_URL[1:]), protected_serve, {'document_root': settings.MEDIA_ROOT}),
)
In my case I edited it to the following because my directories are set up differently and I use Login Required Middleware to ensure login is required everywhere (Django: How can I apply the login_required decorator to my entire site (excluding static media)?:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', "django.views.static.serve", {'document_root': settings.MEDIA_ROOT}),
)

django-allauth configuration doubts

I'm using django-allauth with Django 1.5.1 and I have a few questions when setting it up:
1. Configure urls.py
The docs says that you have to add the following to urls.py file:
urlpatterns = patterns('',
...
(r'^accounts/', include('allauth.urls')),
...
)
The problem is that I already have a custom app called accounts and I already use the following URL pattern:
(r'^accounts/', include('accounts.urls')),
So I have a naming collision here with the accounts/ regex URL. My question is: can I rename the allauth URL pattern to (r'^auth/', include('allauth.urls')) without having problems, or is it unsafe to do so and it'd be better to rename my own URL to something like (r'^users/', include('users.urls')) (and rename my accounts app to users for naming consistency).
2. Customize allauth default templates
What is the proper way to customize the default templates for login, etc.? I think that modifying the library directly is not the best approach. I guess it should be done through templates directory using some concrete directory hierarchy. Also, I don't know if some kind of base.html file must be provided to extend from when overriding these templates or the site's base.html that all pages extend can be used without problems. Could you illustrate me with this?
3. Admin login form shows logins and logouts the first time it's accessed
When I access the admin panel after some logins and logouts the history appears, but if I refresh the page then it disappears. I think this must be something related with the django messages:
4. Setting SOCIALACCOUNT_PROVIDERS
Is the dictionary setting called SOCIALACCOUNT_PROVIDERS optional or must it be set?
5. How is the password calculated when a user signs in with a 3rd party app?
When the user is created it has a password, but how is it calculated? And... is it useful or is it only a placeholder for this required field? Can the user use it to local login?
Thanks!
With respect to 1):
There is no collision as long as there is no overlap in the fully matched URL patterns. For example: if your accounts app has a match for "/accounts/login/" then there is indeed a collision as allauth is gunning for that URL as well. But, if your accounts app simply matches other URLs with /accounts/ as prefix then you are fine.
If you insist, you can indeed put allauth URLs below a different path. allauth uses name based URL reversal, so the new path prefix will be picked up automatically.
As for 2):
There is nothing special about allauth templates. You can override them just like you would for any other Django app.
Have a look at the example app. It has both Bootstrap and uniform template overrides. They can be enabled by uncommenting this line: https://github.com/pennersr/django-allauth/blob/901485557d4ddee30fed920f2159cdf499c39e1c/example/example/settings.py#L126
All allauth templates inherit from a base template, called base.html. I would expect that your project also has a base template. Either override the base.html with yours, or, override base.html with a template that extends from yourbase.html
3): allauth uses the Django messages framework. See:
https://docs.djangoproject.com/en/dev/ref/contrib/messages/#expiration-of-messages -- if you do not iterate over the messages in order to display them, they do not expire. So apparently you are not showing the messages in your templates. Therefore, they heap up until the admin appears which renders (and clears) all messages collected so far...
4) Optional
5) There is no password set, meaning, the user can only login using the 3rd party account until he actually sets a password (/accounts/password/set/).

How to use Django-filer(asset file manager)?

Can someone point me in the right direction on how to use Django-filer? I have installed the necessary tools for the filer to work and I get the Filer tab in the admin interface, but when I upload an image to the app I am not able to get the preview thumbimage. The image is also uploaded to the Media_root path(Public files default path). What else have I missed?
Had a similar problem. I had missed that when easy_thumbnails is automatically pulled in by pip, you still have to explicitly add it to your INSTALLED_APPS in settings.py.
I spotted this because when I was uploading images, they weren't appearing in the clipboard, and when inspecting the response from the upload, there was an internal server error complaining that an easy_thumbnails table wasn't available.
tl;dr; check you added easy_thumbnails to your settings and did a migration / syncdb
Take a look in your urls.py. You should have the following code in it:
if settings.DEBUG:
urlpatterns += patterns('', (
r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}))
Cheers

How to configure the webserver to server a particular page as the default page?

I am using Lighttpd and Django. I have configured my Lighttpd server to pass all the requests ending with ".psp" extension to Django.
My startup page is a page served through Django, which is accessed as "http://192.168.1.198/home.psp". I want to enable the user to browse this page without writing "home.psp" explicitly in the url i.e. using "http://192.168.1.198"
Is this possible?
Thanks for any help in advance.
I think you're confusing concepts here between the "old" method of having individual files represent web pages which themselves contain code that is passed off to an interpreter before being sent in a response to how django/frameworks work.
If you're familiar with apache, imagine django as in part taking on the role of mod_rewrite. Django, and other frameworks, have what's called a dispatcher, or routing, mechanism.
Basically, they subscribe to the MVC pattern that says you should separate out the model, controller and view (in django parlance, model, template and view).
Now what then happens is you have a file called urls.py in django, which contains a list of routes (urls) and names of methods (usually contained in views.py) which handle them. Here's an example:
urlpatterns = patterns('',
url(r'^dologin$', 'testapp.views.auth_login', name="auth-login-uri"),
url(r'^doopenidlogin$', 'testapp.views.auth_openid_login', name="auth-openid-login-uri"),
url(r'^dologout$', 'testapp.views.auth_logout', name="auth-logout-uri"),
url(r'^login$', 'testapp.views.loginform', name="login-form"),
url(r'^openidlogin$', 'testapp.views.openidloginform', name="openid-login-form"),
url(r'^$', 'testapp.views.index', name="index"),
)
Here testapp is a python package, views.py is a python file and index is a django view. The url is constructed from regex, so I can have whatever I want as the url, much how stackoverflow urls are formed.
So basically, you never need file extensions again. I'd strongly suggest getting a good book on django - there are a few around.
What you might be looking for is the index-file.names directive in Lighty's configuration file. Just add "home.psp" to the list in your configuration file, and Lighty will look for it when no filename is specified.
I solved the problem myself. Here is what I did:
I had to add a statement inside url.rewrite-once block of lighttpd's configuration file like:
url.rewrite-once = (
"^(/media.*)$" => "$1",
"^(/static.*)$" => "$1",
"^/favicon\.ico$" => "/media/favicon.ico",
"^(/)$" => "/my_project_dir/home.psp",
"^(/.*)$" => "/my_project_dir$1",
)
Apart from this, I added the following line in my urls.py:
(r'^$',my_index_view_name),
Hope this helps someone in the future. Thanks everybody for your replies above. Cheers!

Webfaction apache + mod_wsgi + django configuration issue

A problem that I stumbled upon recently, and, even though I solved it, I would like to hear your opinion of what correct/simple/adopted solution would be.
I'm developing website using Django + python. When I run it on local machine with "python manage.py runserver", local address is http://127.0.0.1:8000/ by default.
However, on production server my app has other url, with path - like "http://server.name/myproj/"
I need to generate and use permanent urls. If I'm using {% url view params %}, I'm getting paths that are relative to / , since my urls.py contains this
urlpatterns = patterns('',
(r'^(\d+)?$', 'myproj.myapp.views.index'),
(r'^img/(.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT + '/img' }),
(r'^css/(.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT + '/css' }),
)
So far, I see 2 solutions:
modify urls.py, include '/myproj/' in case of production run
use request.build_absolute_uri() for creating link in views.py or pass some variable with 'hostname:port/path' in templates
Are there prettier ways to deal with this problem? Thank you.
Update: Well, the problem seems to be not in django, but in webfaction way to configure wsgi. Apache configuration for application with URL "hostname.com/myapp" contains the following line
WSGIScriptAlias / /home/dreamiurg/webapps/pinfont/myproject.wsgi
So, SCRIPT_NAME is empty, and the only solution I see is to get to mod_python or serve my application from root. Any ideas?
You shouldn't need to do anything special. Django honours the SCRIPT_NAME environment variable that is set by mod_wsgi when you serve a Django site other than from the root, and prepends it to the url reversing code automatically.
If you're using mod_python (you shouldn't be), you may need to set django.root in your Apache configuration.
Updated I suspect this is due to the way that Webfaction serves Django sites via a proxy instance of Apache - this instance has no knowledge of the actual mount point as determined by Webfaction's control panel.
In this case, you'll probably need to set SCRIPT_NAME manually in your .wsgi script. I think this should work:
_application = django.core.handlers.wsgi.WSGIHandler()
def application(environ, start_response):
os.environ['SCRIPT_NAME'] = '/myproj/'
return _application(environ, start_response)
Change:
WSGIScriptAlias / /home/dreamiurg/webapps/pinfont/myproject.wsgi
to:
WSGIScriptAlias /myproj /home/dreamiurg/webapps/pinfont/myproject.wsgi
Then change the nginx front end configuration of WebFaction to proxy to '/myproj' on back end instead of '/'.
That should be all that is required. You should not use '/myproj' prefix in urls.py.
In other words, just ensure the mount point for back end is same as where it appears mounted at the front end.
Modify WSGI script file to fudge SCRIPT_NAME, although it may work, is not generally recommended as not allowing Apache/mod_wsgi to do the proper thing, which may have other implications.

Categories