How can I use django.core.files.File - django

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.

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 admin super(type, obj)

Well, let me explain this.
I am working on a simple django admin project.
In the admin.py file, I have the following admin classes:
class A_Admin(admin.ModelAdmin):
#some stuff
class B_Admin(admin.ModelAdmin):
#some stuff
I want to override the get_urls() method of A_Admin that if I click a button on A_Admin instance change page, it will redirect the page to B_Admin changelist page.
(I know there are many ways to do what I want and what I mentioned above is not the best, but this is what I want. So let skip the discussion why I insist on this solution.)
I want to the following:
def get_urls(self):
#django's code
#inside the urlpattern
urlpattern = (
#default urls from django admin
.....
url(r'^some_url$',
wrap(super(B_Admin, self).changelist_view),
name='%s_%s_delete' % info),
....)
return urlpatterns
This is not working, since 'self' is a A_Admin class object rather than B_Admin obejct.
So is there any way to get the proxy of calss A_Admin inside B_Admin?
I just wanna override changelist_view of A and call it inside B.
Is this possible?
Thanks in advance
You should just instantiate B_Admin and use its method.
I believe the following code should work:
from django.contrib import admin
from my_app.models import B_Model # The model for which B_Admin is used
def get_urls(self):
#django's code
#inside the urlpattern
urlpattern = (
#default urls from django admin
.....
url(r'^some_url$',
wrap(B_Admin(B_Model, admin.site).changelist_view),
name='%s_%s_delete' % info),
....)
return urlpatterns
UPDATE: Most probably, B_Admin was already instantiated when you called
admin.site.register(B_Model, B_Admin)
So instead of doing
B_Admin(B_Model, admin.site)
again you can just get it from the AdminSite's registry:
admin.site._registry[B_Model]

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 to Email a Django FileField as an Attachment?

I need to send a models.FileField as an email attachment using Django. I've seen snippets that show how to do this with the raw request.FILES data (which still contains the Content-Type), but have not been able to find anything that shows how to do it once you've already saved the file in a models.FileField. The content type seems to be inaccessible from the models.FileField.
Can someone give me an example of how this would work? I'm beginning to think that I might have to store the Content-Type in the model when I save the file.
Thanks!
Attaching a models.FileField file to an email message is nice and simple in Django:
from django.core.mail import EmailMultiAlternatives
kwargs = dict(
to=to,
from_email=from_addr,
subject=subject,
body=text_content,
alternatives=((html_content, 'text/html'),)
)
message = EmailMultiAlternatives(**kwargs)
message.attach_file(model_instance.filefield.path)
message.send()
I am using django-storages and so .path raises
NotImplementedError: This backend doesn't support absolute paths.
To avoid this, I simply open the file, read it, guess the mimetype and close it later, but having to use .attach instead of .attach_file magic.
from mimetypes import guess_type
from os.path import basename
f = model.filefield
f.open()
# msg.attach(filename, content, mimetype)
msg.attach(basename(f.name), f.read(), guess_type(f.name)[0])
f.close()
Another approach:
from django.core.mail.message import EmailMessage
msg = EmailMessage(subject=my_subject, body=my_email_body,
from_email=settings.DEFAULT_FROM_EMAIL, to=[to_addressed])
msg.attach_file(self.my_filefield.path) # self.my_filefield.file for Django < 1.7
msg.send(fail_silently=not(settings.DEBUG))
I would just not supply a content type and let the recipient's email client work it out. Unless it will be something unusual it shouldn't be a problem.
RFC2616 states:
If and only if the media type is not
given by a Content-Type field, the
recipient MAY attempt to guess the
media type via inspection of its
content and/or the name extension(s)
of the URI used to identify the
resource.
but...
If you want to specify it then storing the content type on upload is a very good idea. It should be noted that django's own docs say to verify the data from users
If you are on a *unix OS you could try to guess/inspect it:
import subprocess
subprocess.check_output(['file', '-b', '--mime', filename])
(from How to find the mime type of a file in python? )

Django admin proper urls inside listview

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