I am uploading a file and saving it on a folder outside the media folder. I want to add a download link in a template to be able to download the file directly. I am not sure what to add in the URL, if I should add something
I tried this in the template it says URL not found
Download File
views.py
def uploaddata(request):
if request.user.is_authenticated:
if request.method == 'POST':
form = uploadform(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('file_list')
else:
form = uploadmetaform()
return render(request, 'uploaddata.html', {
'form': form
})
else:
return render(request, 'home.html')
HTML page
<tbody>
{% for data in dataset %}
<tr>
<td>{{data.path_id}}</td>
<td>{{ data.tar_gif }}</td>
<td>
Download File
</td>
</tr>
{% endfor %}
</tbody>
models.py
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
Assigned_Group= models.CharField(max_length=500, choices=Group_choices, default='Please Select')
def __str__(self):
return self.user.username
upload file
def nice_user_folder_upload(instance, filename):
extension = filename.split(".")[-1]
return (
f"{instance.user_profile.Assigned_Group}/{filename}"
)
class uploadmeta(models.Model):
path = models.ForeignKey(Metadataform, on_delete=models.CASCADE)
user_profile = models.ForeignKey(UserProfile, on_delete=models.CASCADE, null=True, verbose_name='Username')
document = models.FileField(upload_to=nice_user_folder_upload, verbose_name="Dataset") # validators=[FileExtensionValidator(allowed_extensions=['tar', 'zip'])]
def __str__(self):
return self.request.user
Actually simple way of doing it by using html download attribute
the way you achieve this is by
<a href="{{data.document.url}}" download> Download File</a>
or you also use :
Download File
1.settings.py:
MEDIA_DIR = os.path.join(BASE_DIR,'media')
#Media
MEDIA_ROOT = MEDIA_DIR
MEDIA_URL = '/media/'
2.urls.py:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)
3.in template:
<a href="{{ file.url }}" download>Download File.</a>
Work and test in django >=3
for more detail use this link:
https://youtu.be/MpDZ34mEJ5Y
Look at this post:
How to serve downloadable files using Django (Chris Gregori) ?
Maybe a better way to not expose the path of the files, even the names if you do want to do that. I do not like the idea to show the structure of the paths to users. Other things you can accomplish with this is to validate who can download the files if you check the request.user with your database for every file is served, and it is pretty simple.
Basically, the publication refers to using the xsendfile module so django generates the path to the file (or the file itself), but the actual file serving is handled by Apache/Lighttpd. Once you've set up mod_xsendfile, integrating with your view takes a few lines of code:"
from django.utils.encoding import smart_str
response = HttpResponse(mimetype='application/force-download') # mimetype is replaced by content_type for django 1.7
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(file_name)
response['X-Sendfile'] = smart_str(path_to_file)
# It's usually a good idea to set the 'Content-Length' header too.
# You can also set any other required headers: Cache-Control, etc.
return response
As I said before, I do not like the idea of having published the path to the file.
With this pointer to the information, you can research and get this working.
Hope it works for you.
Related
I have spent over three days on this. It appears that something saved to the db, but when I try to access it in the template, I can only get the object of the db. I can not access it. I believe it was not saved successfully because I don't see a media folder where it would be saved. This is what I have so far.
# settings.py
STATIC_URL = '/static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'), )
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# forms.py
from .models import Pic
from django import forms
class ImageForm(forms.ModelForm):
class Meta:
model= Pic
fields= ["picture"]
# models.py
class Pic(models.Model):
picture = models.ImageField(upload_to = 'media/', default = 'media/None/no-img.jpg', null=True, blank= True)
# views.py
def upload(request):
form = ImageForm
context = {}
userBio = Bio.objects.get(userData = User.objects.get(id = request.session['client']['id']))
if request.method == 'POST':
# .is_valid checks the models parameters via forms method
form = ImageForm(request.POST,request.FILES)
if form.is_valid():
form.save()
print("succesful uploaded image")
return redirect("/dashboard")
else:
print("Not a valid input from form....")
messages.error(request,"Not a valid input")
return redirect("/dashboard")
<!-- dashboard.html -->
<form rule="form" class="border border--secondary" method="POST" action="/manageFile" enctype="multipart/form-data">
{% csrf_token%}
<input type="file" name = "update">
<input type = "submit">
</form>
{% if pictures %}
{% for pic in pictures%}
<img src="{{pic}}" alt="{{pic}}">
{% endfor %}
{% endif%}
In the template, it appears as Pic object(1).
In the terminal, it appears as
Query set Pic: Pic object (1)
Here's what I render to the template.
def dash(request):
try:
_id = request.session['client']['id']
except:
return redirect("/loginPg")
userBio = Bio.objects.get(userData = User.objects.get(id = request.session['client']['id']))
print("its right here")
theUploads = Pic.objects.all()
print("This image object -", theUploads)
content = {
"title" : userBio.title,
"qA" : userBio.quoteA,
"qB" : userBio.quoteB,
"desc" : userBio.desc,
"authorA" : userBio.authorA,
"authorB" : userBio.authorB,
"pictures" : theUploads
}
return render(request, "GoEnigma/dashboard.html", content)
# urls.py
from django.conf.urls.static import static
from django.conf import settings
# urls []
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
This was hard to spot, because you render the form field yourself:
class ImageForm(forms.ModelForm):
class Meta:
model= Pic
fields= ["picture"]
<input type="file" name = "update">
So the name of the file field is update, not picture. Change that to picture and it should work, though it's much better to use django to render the form, so you don't mistakes like this and changes to the form get propagated to the html.
If the picture is being saved in the database than the error is with rendering the template. To render the images in template you need to get the {{model_class.model_fields.url}} so in your case the class is pic, the field is picture.
try this in your template <img src="{{pic.picture.url}}">
put parentheses after ImageForm and add request.POST in ImageForm if it is Post request.
def upload:
form = ImageForm()
if request.method == 'POST':
# .is_valid checks the models parameters via forms method
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
form.save()
print("succesful uploaded image")
return redirect("/dashboard")
else:
print("Not a valid input from form....")
messages.error(request,"Not a valid input")
return redirect("/dashboard")
Also check maybe you haven't added url path for serving media files
so add these following lines in urls.py file of main project.
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
# ..... all regular paths
]
if setting.DEBUG: # if true
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Check official documents here
First of all, I apologize for the noobish question. I'm very new to Django and I'm sure I'm missing something obvious. I have read many other posts here and have not been able to find whatever obvious thing I am doing wrong. Thanks so much for any help, I am on a deadline.
I am using Django 1.6 with Python 2.7. I have one app called dbquery that uses a form to take data from the user and query a REST service. I am then trying to display the results on a results page.
Obviously there is more to add, this is just a very simple start.
The problem is that I can't seem to get the autoincremented id field from my search view into the url tag in the template properly. If I put the number 1 in like this {% url 'dbquery:results' search_id=1 %}, the page loads and works well, but I can't seem to get the variable name right, and the django documentation isn't helping- maybe this is obvious to most people. I get a reverse error because the variable ends up always being empty, so it can't match the results regex in my urls.py. I tested my code for adding an object in the command line shell and it seems to work. Is there a problem with my return render() statement in my view?
urls.py
from django.conf.urls import patterns, url
from dbquery import views
urlpatterns = patterns('',
# ex: /search/
url(r'^$', views.search, name='search'),
# ex: /search/29/results/ --shows response from the search
url(r'^(?P<search_id>\d+)/results/', views.results, name ='results'),
)
models.py
from django.db import models
from django import forms
from django.forms import ModelForm
import datetime
# response data from queries for miRNA accession numbers or gene ids
class TarBase(models.Model):
#--------------miRNA response data----------
miRNA_name = models.CharField('miRNA Accession number', max_length=100)
species = models.CharField(max_length=100, null=True, blank=True)
ver_method = models.CharField('verification method', max_length=100, null=True, blank=True)
reg_type = models.CharField('regulation type', max_length=100, null=True, blank=True)
val_type = models.CharField('validation type', max_length=100, null=True, blank=True)
source = models.CharField(max_length=100, null=True, blank=True)
pub_year = models.DateTimeField('publication year', null=True, blank=True)
predict_score = models.DecimalField('prediction score', max_digits=3, decimal_places=1, null=True, blank=True)
#gene name
gene_target = models.CharField('gene target name',max_length=100, null=True, blank=True)
#ENSEMBL id
gene_id = models.CharField('gene id', max_length=100, null=True, blank=True)
citation = models.CharField(max_length=500, null=True, blank=True)
def __unicode__(self):
return unicode(str(self.id) + ": " + self.miRNA_name) or 'no objects found!'
views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.core.urlresolvers import reverse
from dbquery.models import TarBase, SearchMainForm
from tarbase_request import TarBaseRequest
#main user /search/ form view
def search(request):
if request.method == 'POST': #the form has been submitted
form = SearchMainForm(request.POST) #bound form
if form.is_valid(): #validations have passed
miRNA = form.cleaned_data['miRNA_name']
u = TarBase.objects.create(miRNA_name=miRNA)
#REST query will go here.
#commit to database
u.save()
return render(request,'dbquery/results.html', {'id':u.id})
else: #create an unbound instance of the form
form = SearchMainForm(initial={'miRNA_name':'hsa-let-7a-5p'})
#render the form according to the template, context = form
return render(request, 'dbquery/search.html', {'form':form})
#display results page: /search/<search_id>/results/ from requested search
def results(request, search_id):
query = get_object_or_404(TarBase, pk=search_id)
return render(request, 'dbquery/results.html', {'query':query} )
templates:
search.html
<html>
<head><h1>Enter a TarBase Accession Number</h1>
</head>
<body>
<!--form action specifies the next page to load-->
<form action="{% url 'dbquery:results' search_id=1 %}" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Search" />
</form>
</body>
results.html
<html>
<head><h1>Here are your results</h1>
</head>
<body>
{{query}}
</body
The search results aren't created and don't have an ID until after you submit your form. The usual way to do this would be to have your form use its own URL as the action, then have the view redirect to the results view after successfully saving:
from django.shortcuts import redirect
def search(request):
if request.method == 'POST': #the form has been submitted
form = SearchMainForm(request.POST) #bound form
if form.is_valid(): #validations have passed
miRNA = form.cleaned_data['miRNA_name']
u = TarBase.objects.create(miRNA_name=miRNA)
#REST query will go here.
#commit to database
u.save()
return redirect('results', search_id=u.id)
else: #create an unbound instance of the form
form = SearchMainForm(initial={'miRNA_name':'hsa-let-7a-5p'})
#render the form according to the template, context = form
return render(request, 'dbquery/search.html', {'form':form})
Then in your template:
<form action="" method="post">
That causes your form to submit its data to the search view for validation. If the form is valid, the view saves the results, then redirects to the appropriate results page based on the ID as determined after saving.
In this case, you're likely better off passing your search parameter as a parameter, such as http://host/results?search_id=<your search value>.
This will allow you to specify your URL as url(r'results/', views.results, name ='results') and reference in your template as {% url dbquery:results %}.
Then in your view, you would change it to:
def results(request):
search_id = request.POST.get('search_id')
query = get_object_or_404(TarBase, pk=search_id)
return render(request, 'dbquery/results.html', {'query':query} )
Or if you want the query to actually show in the URL, change the form to be method="get" and the request.POST.get('search_id') to request.GET.get('search_id')
I need some help understanding how to download files from my django site to a users machine. I have already created a fairly basic way to upload the files. The files are then an instance of a class and are saved in my "media" folder. What I need help with is how to serve the files. I want the user to be able to visit a page and click on the files they want to download. For some reason I can't find any resources on how to do this. Here are my files at this point
urls.py
url(r'^admin/', include(admin.site.urls)),
url(r'^upload/', views.upload),
url(r'^download/', views.download),
url(r'^success/', views.success),
)
if settings.DEBUG:
urlpatterns = patterns('',
url(r'^media/(?P<path>.*)$', 'django.views.static.serve',{'document_root': settings.MEDIA_ROOT, 'show_indexes': True}),
) + urlpatterns
models.py
class WorkSheet(models.Model):
worksheet_name = models.CharField(max_length= 150, default = '')
creator = models.ForeignKey(User, default = True)
worksheet_file = models.FileField(upload_to = 'worksheets', default = True)
number_of_stars = models.PositiveIntegerField(default = 0)
category = models.CharField(max_length = 100, default = '')
class UploadWorkSheetForm(ModelForm):
class Meta:
model = WorkSheet
exclude = ('number_of_stars',
'creator',)
views.py
def upload(request):
if request.method == 'POST':
form = UploadWorkSheetForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return http.HttpResponseRedirect('/success/')
else:
form = UploadWorkSheetForm()
return render(request, 'upload.html', {'form': form})
def download(request):
return render_to_response('download.html')
def success(request):
return render_to_response('upload_success.html')
So basically I want the user to visite www.mysite.com/download and be able to download a file. Thank you!!
.
.
.
.
.
.
.
.
.
Also, is it a problem that my upload file view does not have a handler like this?
def handle_uploaded_file(file,path=''):
filename = file._get_name()
destination_file = open('%s/%s' % (settings.MEDIA_ROOT, str(path) + str(filename)), 'wb+')
for chunk in file.chunks():
destination_file.write(chunk)
destination_file.close()
In your download view, you are just rendering a download.html, but you are not sending any contexts to it. I would may be send a queryset of worksheets,(Worksheet.objects.all()) as a context to the template. And in the template, do something like
{% for worksheet in worksheets %}
{{ worksheet.worksheet_file.url }}
{% endfor %}
Then you would have all the file urls present in your WorkSheet.objects.all() query.
If possible I would handle all the upload logic in the models file itself, something like this
I am a new Djangoer, and figuring out how to build custom widget, my problem is cannot get the MEDIA_URL in my widget's template, while the form use MySelectWidget able to get the MEDIA_URL itself.
#
#plus_sign.html
#
<a href="" class="" id="id_{{ field }}">
<img src="{{ MEDIA_URL }}images/plus_sign.gif" width="10" height="10" alt="Add"/>
</a>
^ cannot load the {{ MEDIA_URL}} to this widget's template, and therefore I can't load the .gif image properly. :(
#
#custom_widgets.py
#
from django import forms
class MySelectMultiple(forms.SelectMultiple):
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
plus = render_to_string("plus_sign.html", {'field': name})
return html+plus
#
#forms.py
#
from django import forms
from myapp.custom_widgets.py import MySelectMultiple
class MyForm(forms.ModelForm):
contacts = forms.ModelMultipleChoiceField(Contact.objects, required=False, widget=MySelectMultiple)
#
#views.py
#
def AddContacts(request):
if request.method == 'POST':
form = MyForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
new = form.save()
return HttpResponseRedirect('/addedContact/')
else:
form = MyForm()
return render_to_response('shop/my_form.html', {'form': form}, context_instance=RequestContext(request))
#
#my_form.html
#
{% extends "base.html" %}
{% block content %}
{{ form.contacts }}
{% endblock %}
Please let me know how can I load the widget's image properly. Thank you so much for all responses.
Context processors only get applied when you use a RequestContext.
Your render method should be something like:
from django.template import RequestContext
def render(self, name, *args, **kwargs):
html = super(MySelectMultiple, self).render(name, *args, **kwargs)
context = RequestContext({'field': name})
plus = render_to_string("plus_sign.html", context)
return html + plus
And, as was mentioned by #czarchaic, make sure the media context processor is in TEMPLATE_CONTEXT_PROCESSORS (it should be by default).
Docs link.
Actually the correct way to do this is using Widget Media.
When defining your widget, you should define a Media inner class in which you should include a CSS file in order to style your widget. In this case make the <a> tag not to display text and have a plus sign background image.
class MyWidget(TexInput):
...
class Media:
css = {
'all': ('my_widget.css',)
}
If you really need to include the MEDIA_URL inside your rendered widget, I'd recommmend to import it directly from django.conf.settings and include settings.MEDIA_URL in your rendering context.
from django.conf import settings
class MyWidget(TextInput):
...
def render(self):
return render_to_string('my_widget.html', {
'MEDIA_URL': settings.MEDIA_URL,
...
})
Make sure the context processor is being loaded in settings.py
TEMPLATE_CONTEXT_PROCESSORS=(
...other processors,
"django.core.context_processors.media",
)
It is loaded by default if you don't specify TEMPLATE_CONTEXT_PROCESSORS, but if specified, the above processor must also be included.
http://docs.djangoproject.com/en/dev/ref/settings/#template-context-processors
I think we can do in this way, to pass the RequestContext, in order to access the MEDIA_URL without making another variable, and passing other variables at the 2nd parameter of the render_to_string method.
If we use:
context = RequestContext({'field': name})
The {{ field }} in the widget's template is empty and not able to access.
Here is the block which can access the MEDIA_URL as well as the {{ field }}.
However, I agree using the inner Media class for complex javascript and CSS setting. However, for a simple image src path, I think this will do.
def render(self, name, *args, **kwargs):
html = super(SelectMultipleWithModalDialog, self).render(name, *args, **kwargs)
**context = RequestContext({})
popup_plus = render_to_string("widgets/modal_dialog_plus_sign.html", {'field': name}, context_instance=context)**
return html + popup_plus
Please correct me if this is not the good way of doing it. Thanks for all participants of this thread.
Howdy - I've written a very simple app to accept job applications including a resume upload.
Running the bundled server for development locally, I can successfully upload files via the web form on the front end and the admin interface. Running it on the remote server (Apache with mod_python) I can successfully upload files via the admin interface but attempts over the web front end yield no uploaded file.
I've added FILE_UPLOAD_PERMISSIONS = 0644 to settings, checked the two settings files, and looked for similar problems described elsewhere. Figure I'm either forgetting a setting or need to go about this a different way. Any suggestions?
For reference, code included.
The model:
class Application(models.Model):
job = models.ForeignKey('JobOpening')
name = models.CharField(max_length=100)
email = models.EmailField()
date_applied = models.DateField()
cover_letter = models.TextField()
resume = models.FileField(upload_to='job_applications', blank=True)
def __str__(self):
return self.name
def save(self):
if not self.date_applied:
self.date_applied = datetime.today
super(Application, self).save()
The form:
class JobApplicationForm(ModelForm):
class Meta:
model = Application
def save(self, commit=True, fail_silently=False):
super(JobApplicationForm, self).save(commit)
The view:
def job_application(request):
ajax = request.GET.has_key('ajax')
if request.method == 'POST':
form = JobApplicationForm(request.POST, request.FILES)
if form.is_valid():
new_application = form.save()
return HttpResponseRedirect('/about/employment/apply/sent/')
elif request.GET.has_key('job'):
job = request.GET['job']
form = JobApplicationForm({'job': job})
else:
return HttpResponseRedirect('/about/')
t = loader.get_template('employment/job_application.html')
c = Context({
'form': form,
})
return HttpResponse(t.render(c))
You don't show the template. If I had to guess, seeing as the upload works via the admin interface, I'd say you've forgotten to put the enctype in your form tag:
<form enctype="multipart/form-data" method="post" action="/foo/">
First, Have you made sure your template has the enctype="multipart/form-data" flag in it?
<form action="." method="POST" enctype="multipart/form-data">
...
</form>
First, there's no need to override save() in your ModelForm since you're not doing any extra work in it.
Second, there's no need to store the new_application variable, simply call form.save().
Third, you should be using a slug field in your JobOpening model and passing that in the querystring. Remember, this isn't PHP, use pretty urls like /jobs/opening/my-cool-job-opening/, that's what slugs are for; unique human readable urls. Your GET code in your view is very fragile as it stands.
Finally, you may want to use the render_to_response shortcut function as it will save you having to verbosely call template loaders, create context and render them manually.