Django admin proper urls inside listview - django

My current target is to give users the chance to download CSV files from the admin site of my application.
I successfully managed to create an additional column in the model's list view this way:
def doc_link(self):
return '%s' % (self.output, self.output)
doc_link.allow_tags = True
This shows the file name and creates the link, but sadly - because it's inside my 'searches' view - it has an URL:
my_site/my_app/searches/files/13.csv.
This is my problem, I would like to have my files stored in the admin media directory, like this:
http://my_site/media/files/13.csv
Does somebody know how to give url which points "outer" from the model's directory?
Maybe somehow tell Django to use the ADMIN_MEDIA_PREFIX in the link?
I'd really appreciate any help, thanks!

I feel like you answered your own question : )
What's stopping you from using ADMIN_MEDIA_PREFIX if you want to?
I think it's strange you would use ADMIN_MEDIA_PREFIX since that's where your admin media lives -- you shouldn't be saving anything there, so maybe more like your MEDIA_URL.
from django.conf import settings
def doc_link(self):
return '%s' % (settings.MEDIA_URL, self.output, self.output)
doc_link.allow_tags = True

Related

Dynamic image upload/browsing path for django-tinymce

I would like to use tinyMCE as the editor for my django application, but have run into some trouble. I have everything setup, but it appears there is no way to specify the upload path for the image insert/upload function. I have two specific scenarios where this becomes a problem:
File browser for user submitted content should not show files uploaded by other users. I'd like to "jail" them to a specific directory by user id.
I need a way to link uploaded files with the object they are related to. That way I can prune those files in the future if the object no longer exists, and I can show only images that are related to that object in the filebrowser.
django-tinymce-filebrowser automatically sets the upload path to mce_filebrowser/%Y/%m/%d. There doesn't appear to be an option to change this path in any way.
django-filebrowser has options for setting the upload directory in settings.py, but I haven't been able to find any record of someone overriding that path for a specific modelform instance. The closest I found was Django filebrowser, model specific directory parameter for FileBrowserField, but I think the solution there isn't applicable to my situation.
Is anyone using another filebrowser for django-tinymce? Did you have a similar problem and find a fix for it? I'd appreciate any points in the right direction.
I used a similar approach but instead of modifying the django-filebrowser code I ended up extending the browse() method in a subclass of FileBrowserSite and making the modification there:
from django.core.files.storage import DefaultStorage
from filebrowser.sites import FileBrowserSite
class FileBrowserSite(FileBrowserSite):
def browse(self, request):
self.directory = self.directory + str(request.user) + '/'
return super(FileBrowserSite, self).browse(request)
storage = DefaultStorage()
site = FileBrowserSite(name='file', storage=storage)
site.directory = "content/"
I put this piece of code on a file called filebrowser.py and the then on my urls.py I did:
from .filebrowser import site
urlpatterns = [
url(r'^admin/content/file/', include(site.urls)),
]
I think is a much cleaner approach than modifying the source code, and is working like charm on my project.
I've extended a little bit the answer by Erasmo. Generally, it works great. Thanks! However, as OriolJ pointed out, every user needs a created directory to use the filebrowser. It is recommended to avoid using signals, so I added the functionality to the custom FileBrowserSite.
filebrowser.py
import os
from django.conf import settings
from django.core.files.storage import DefaultStorage
from filebrowser.sites import FileBrowserSite
class FileBrowserSite(FileBrowserSite):
def browse(self, request):
# get directory path from settings to avoid recursion
self.directory = settings.DIRECTORY + str(request.user) + '/'
# create a directory for a user if it does not already exist
full_path = self.storage.location + '/' + self.directory
if not os.path.exists(full_path):
os.makedirs(full_path)
return super().browse(request)
storage = DefaultStorage()
site = FileBrowserSite(name='file', storage=storage)
settings.py
FILEBROWSER_DIRECTORY = 'user_content/'
DIRECTORY = ''
urls.py
from .filebrowser import site
urlpatterns = [
url(r'^admin/content/file/', include(site.urls)),
]
Hope this slight update will save someone a couple of minutes.
I've, somewhat hacked this. I'm on grappelli 2.7.2, and django-filebrowser 3.6.1.
I've just added a line to the django-filebrowser file: sites.py:
on the method browse() of the class FileBrowserSite, line ~273, I've added:
self._directory_set(DIRECTORY + str(request.user) + "/")
It's important though that there is already a directory created for every user, you can make a post_save signal to create a directory every time a user is created. This solution will not relate the files with the object, but I think that this may be a good start for you and it will isolate files of different users.
For me, this is currently working just fine, I can imagine that this is not the most perfect way to do it, feedback is very welcome.

Django: Get ImageField url in view

I am trying to get ImageField absolute path in Django view so I can open the image, write on it something and then serve it to user.
I have problem getting absolute path for the image which is saved in media folder.
item = get_object_or_404(Item, pk=pk)
absolute_url = settings.MEDIA_ROOT + str(item.image.path)
I get error, that item.image does not have path (The 'banner' attribute has no file associated with it.). item.image is ImageField and I'd like to know how to get absolute path of the image saved in image field (within Django view).
When configured correctly, use .url: item.image.url. Check the docs here.
Maybe this way?
#property
def get_absolute_image_url(self):
return "{0}{1}".format(settings.MEDIA_URL, self.image.url)
see How can I get a list of absolute image URLs from a Django QuerySet?
I just figured out what was the problem... absolute image path is saved in file variable.
Example (in view):
item = Item.objects.get(pk=1)
print item.file # prints out full path of image (saved in media folder)

Django running wrong view method

I have to view files stored in mysite folder. one is named as views.py and other is named as request_view.py. In urls.py, I have used 'answer' method for views.py and 'display_meta' method for request_view.py.
(django version: 1.5 and python version: 2.7.3)
this is the url pattern:
url(r'^twitter/$', answer), url(r'request/$', display_meta)
when I call http:/127.0.0.1:8000/request/, then also first view(i.e. /twitter/) is called!
any help?
one more thing. In my view.py, I have some unbounded code (i.e. the code which is neither present in a method or class). can this be the cause of the problem?
l = StdOutListener()
auth = OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
stream = Stream(auth, l)
keyword = input('enter the keyword you want to search for?')
stream.filter(track = [keyword])
apart from this code, evry code is either in the class or method.
One thing that I noticed is that first the code of the view.py runs, then display_meta runs.
Thanks in advance.
SOLVED
The problem was with the import function that I was using. since my code was unbounded in one of the views, the import function always import that regardless of the url that I choose.
Suggestion
Always use the nomenclature mentioned in the this example. In many books it has been suggested that we should import the views, but it might cause an error if you have unbounded code like I had.
I don't know exactly why /twitter/ view is called, but I can see two things to change:
You should use a string as the second parameter for url(), as you can see in this example [1]. You can use 'myapp.views.my_method' nomenclature.
You forgot to start the request URL with ^ that indicates the start of the URL.
About the unbounded code, I don't know if that could be causing the problem. But I can't see why are you putting that code unbounded. I am not sure when that code would be executed, I guess the first time you call a view in that file and Django loads the file (I'm guessing, I don't know exactly), but I don't think that would be a good way to do that. Think when do you want to execute that code, put it in a method, and call it.
[1] https://docs.djangoproject.com/en/1.5/topics/http/urls/#example
HI hemant i am wondering why you have written request_view.py.
Please see the django docs.
what you can do is .
Create two function in your views.py like
def answer(request):
do some stuffs.
render_to_response(template.html)
and on the same page write another
def display_meta(request):
# do your studd
render_to_response(some.html)
YOU NEED NOT TO CREATE TWO SEPERATE VIEWS.PY
I dont know what this code does
l = StdOutListener()
auth = OAuthHandler(consumer_key, consumer_secret)
auth.set_access_token(access_token, access_token_secret)
stream = Stream(auth, l)
keyword = input('enter the keyword you want to search for?')
stream.filter(track = [keyword])
But if you want to use StdOutListener inside your function you can call in your view
like
def display_meta(request):
stobject = StdOutListener() # use parameters if you have here
# do your studd
render_to_response(some.html)
Organizing views into a python package could solve this problem. So if you have a structure like this...
# views.py
def SomeView(request):
return HttpResponse('SomeView')
# another_view.py
def AnotherView(request):
return HttpResponse('AnotherView')
Your can reorganize these separate view files into a views package. That is...
# views
# __init__.py
from views import SomeView
from another_view import AnotherView
# views.py
def SomeView(request):
return HttpResponse('SomeView')
# another_view.py
def AnotherView(request):
return HttpResponse('AnotherView')
And now, everything can be called in a django-standard way:
url(r'^url-to-some-view/$', 'views.SomeView'),
url(r'^url-to-another-view/$', 'views.AnotherView'),
UPDATED:
To make a 'python package'...
Create a views directory at the same level as the view.py file [mkdir views]
Create a __init.py__ file inside the views directory # this is what makes a directory a 'python package'
Move views.py into the views directory.
Move your request_view.py into the views directory.
Edit the __init__.py file with the necessary import statements. In this case:
from views import answer
from request_view import display_meta
What this does is replace a file with a directory. By importing everything into the __init__.py file, this directory looks like a large file to your code, rather than another module.

How do I serve a text file from Django?

I am writing a Django based website, but need to serve a a simple text file. Is the correct way to so this by putting it in the static directory and bypassing Django?
If the file is static (not generated by the django app) then you can put it in the static directory.
If the content of this file is generated by Django then you can return it in a HttpResponse with text/plain as mimetype.
content = 'any string generated by django'
return HttpResponse(content, content_type='text/plain')
You can also give a name to the file by setting the Content-Disposition of the response.
filename = "my-file.txt"
content = 'any string generated by django'
response = HttpResponse(content, content_type='text/plain')
response['Content-Disposition'] = 'attachment; filename={0}'.format(filename)
return response
I agree with #luc, however another alternative is to use X-Accel-Redirect header.
Imagine that you have to serve big protected (have to login to view it) static files. If you put the file in static directory, then it's access is open and anybody can view it. If you serve it in Django by opening the file and then serving it, there is too much IO and Django will use more RAM since it has to load the file into RAM. The solution is to have a view, which will authenticate a user against a database, however instead of returning a file, Django will add X-Accel-Redirect header to it's response. Now since Django is behind nginx, nginx will see this header and it will then serve the protected static file. That's much better because nginx is much better and much faste at serving static files compared to Django. Here are nginx docs on how to do that. You can also do a similar thing in Apache, however I don't remember the header.
I was using a more complex method until recently, then I discovered this and this:
path('file.txt', TemplateView.as_view(template_name='file.txt',
content_type='text/plain')),
Then put file.txt in the root of your templates directory in your Django project.
I'm now using this method for robots.txt, a text file like the original asker, and a pre-generated sitemap.xml (eg, change to content_type='text/xml').
Unless I'm missing something, this is pretty simple and powerful.
I had a similar requirement for getting a text template for a form via AJAX. I choose to implement it with a model based view (Django 1.6.1) like this:
from django.http import HttpResponse
from django.views.generic import View
from django.views.generic.detail import SingleObjectMixin
from .models import MyModel
class TextFieldView(SingleObjectMixin, View):
model = MyModel
def get(self, request, *args, **kwargs):
myinstance = self.get_object()
content = myinstance.render_text_content()
return HttpResponse(content, content_type='text/plain; charset=utf8')
The rendered text is quite small and dynamically generated from other fields in the model.

How can I use django.core.files.File

The docs for django.core.files.File imply I can do this: print File(open(path)).url but the File object has no attribute 'url' However, django.db.models.fields.files.FieldFile extends File and does have all the attributes described in the docs for File, but I can't create one without giving it a model field.
All I want it something that does what the docs for django.core.files.File (link above) say it does, take a python file and give it attributes like 'url' and 'path' and 'name', can anyone help?
Cheers, Jake
Regardless of what the Django doc says, if you look at the code for the File class, I don't see it there. Following Ignacio's suggestion, you can derive from the Django File and use the MEDIA_ROOT and MEDIA_URL settings to implement the property you're looking for...
from django.core.files import File
from django.conf import settings
class UrlFile(File):
def _get_url(self):
root_name = self.name.replace(settings.MEDIA_ROOT, '')
return '%s%s' % (settings.MEDIA_URL, root_name)
url = property(_get_url)
If you derive from file then you can give it any attributes you like:
class MyFile(file):
def foobar(self):
print 'foobar'
f = MyFile('t.txt', 'r')
f.foobar()
Thanks Guys. I've solved my problem by writing a custom class, it's not as powerful as the Django one would be if I could use it, but it works for my current application. I'll open a ticket about the docs.