Download Files in Django - django

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

Related

How to Upload an Image to Django

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

django : url change depends on model exist

I'd like to make user access to page depends on model exist. and I use CBV.
Do I have to control views? or urls?
Is FBV only way to control url?
How can I control user access url? hope kindly help me.
i'd like to control for example:(as you know, this is invalid syntax. hope you know what i'm saying.)
from django.urls import path
from . import views, models
app_name = "papers"
urlpatterns = [
path(
"memberagreement/<int:preassociation_pk>/",
{% if models.MemberAgreement.association.get(pk=preassociaion_pk) is not NULL %}
views.member_agreement_detail_vc.as_view(),
{% else %}
views.member_agreement_create_vc.as_view(),
{% endif %}
name="member_agreement_vc",
)
]
I add my views.py:(it works when models is exist, but if model does not exist, i can't load my template...)
def member_agreement_vc(request, preassociation_pk):
preassociation = preassociation_models.Preassociation.objects.get(
pk=preassociation_pk
)
try:
member_agreement = models.MemberAgreement.objects.get(pk=1)
return render(
request,
"papers/member_agreement/detail.html",
{"member_agreement": member_agreement},
)
except models.MemberAgreement.DoesNotExist:
form_class = forms.CreateMemberAgreementFormVC
template_name = "papers/member_agreement/create.html"
def form_valid(self, form):
pk = self.kwargs.get("preassociation_pk")
member_agreement = form.save()
# content
# association
# writer
# participants
# category
# is_business
# is_general
# number_of_investment_account
# name
# resident_registration_number
# address
# contact
member_agreement.writer = self.request.user
member_agreement.association = preassociation_models.Preassociation.objects.get(
pk=pk
)
member_agreement.category = "member_agreement"
member_agreement.is_business = True
member_agreement.is_general = False
member_agreement.save()
form.save()
return redirect(
reverse(
"preassociations:paper",
kwargs={"pk": member_agreement.association.pk},
)
)
What you are asking, I think is not possible. URL is just a path, or a way you will do nothing in there except walking. Just like you are new to a road, you will just walk, then when you have to decide where to go, either you will ask google to do so, or the nearby person. So, road is like url and google map is like views where you will decide where to go.
I am going to try it the views ways then,
// urls.py
// Add your path
// views.py
from .models import YourModel // import model
def decideWhereToGo(request):
modelExist = YourModel.objects.filter(someField=someValue).exists()
if modelExist:
// Do sthg
else:
// redirect to the url where you want to send if model does not exists

How to download uploaded files through a link in Django

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.

Dropzone with django image upload

Im trying to upload images using Dropzone.js .
There doesnt seem to be any current tutorials for Dropzone although i used this link: https://amatellanes.wordpress.com/2013/11/05/dropzonejs-django-how-to-build-a-file-upload-form/
Essentially what i want to do is , The user uploads multiple images . a hotel_id is attached to the images and stored in the hotelphotos table as a url which is unique each time .
MY code
Models.py
class Photo(models.Model):
hotel = models.ForeignKey(Hotels,on_delete=models.CASCADE)
path = models.FileField(upload_to='files/%Y/%M/%d)
forms.py
class PhotosForm(forms.ModelForm):
class Meta:
model = Photo
fields = ['path']
views.py
def uploadPhoto(request,hotelid):
if request.method == 'POST':
form = PhotosForm(request.POST,request.FILES)
if form.is_valid():
new_file = Photo(path = request.FILES['file'] , hotel_id = hotelid)
new_file.save()
else:
form = PhotosForm()
hotelid = hotelid
data = {'form': form, 'hotelid':hotelid}
return render(request, template , data)
The form
<form class="dropzone" action="{% url 'ManageHotels:uploadPhoto' hotelid %} method = "POST">
</form>
Files uploaded dont get created and the url also doesnt add to the database.
Hope someone can help!

Tango With Django - Fun With Forms URLconf Error

I am currently doing the latest tango with django course and have just about finished '8. Fun with Forms', I however cannot solve an error I am getting. The course tells us to make an add_page form and supplies us with the view and the form, all we really have to do is create the URL, I have done this however I cannot get it to recognize the URL for the add_page view.
Sorry I cannot post pictures as I do not have high enough reputation so I have given links.
This is the error I get
http://i.stack.imgur.com/UrFxv.png
Here is my code
URLS -
from django.conf.urls import patterns, url
from rango import views
urlpatterns = patterns('',
url(r'^$', views.index, name='index'),
url(r'^about/', views.about, name="about"),
url(r'^add_category/$', views.add_category, name='add_category'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/add_page/$', views.add_page, name='add_page'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),
)
VIEWS -
from django.shortcuts import render
from django.http import HttpResponse
from rango.models import Category, Page
from rango.forms import CategoryForm, PageForm
def index(request):
# Query the database for a list of ALL categories currently stored.
# Order the categories by no. likes in descending order.
# Retrieve the top 5 only - or all if less than 5.
# Place the list in our context_dict dictionary which will be passed to the template engine.
category_list = Category.objects.order_by('-likes')[:5]
page_list = Page.objects.order_by('-views')[:5]
context_dict = {'categories': category_list, 'pages': page_list}
# Render the response and send it back!
return render(request, 'rango/index.html', context_dict)
def about(request):
message = "Rango says here is the about page. <a href='/rango/'>Index</a> "
return HttpResponse(message)
def category(request, category_name_slug):
# Create a context dictionary which we can pass to the template rendering engine.
context_dict = {'category_name_slug': category_name_slug}
try:
# Can we find a category name slug with the given name?
# If we can't, the .get() method raises a DoesNotExist exception.
# So the .get() method returns one model instance or raises an exception.
category = Category.objects.get(slug=category_name_slug)
context_dict['category_name'] = category.name
# Retrieve all of the associated pages.
# Note that filter returns >= 1 model instance.
pages = Page.objects.filter(category=category)
# Adds our results list to the template context under name pages.
context_dict['pages'] = pages
# We also add the category object from the database to the context dictionary.
# We'll use this in the template to verify that the category exists.
context_dict['category'] = category
except Category.DoesNotExist:
# We get here if we didn't find the specified category.
# Don't do anything - the template displays the "no category" message for us.
pass
# Go render the response and return it to the client.
return render(request, 'rango/category.html', context_dict)
def add_category(request):
# A HTTP POST?
if request.method == 'POST':
form = CategoryForm(request.POST)
# Have we been provided with a valid form?
if form.is_valid():
# Save the new category to the database.
form.save(commit=True)
# Now call the index() view.
# The user will be shown the homepage.
return index(request)
else:
# The supplied form contained errors - just print them to the terminal.
print form.errors
else:
# If the request was not a POST, display the form to enter details.
form = CategoryForm()
# Bad form (or form details), no form supplied...
# Render the form with error messages (if any).
return render(request, 'rango/add_category.html', {'form': form})
def add_page(request, category_name_slug):
try:
cat = Category.objects.get(slug=category_name_slug)
except Category.DoesNotExist:
cat = None
if request.method == 'POST':
form = PageForm(request.POST)
if form.is_valid():
if cat:
page = form.save(commit=False)
page.category = cat
page.views = 0
page.save()
# probably better to use a redirect here.
return category(request, category_name_slug)
else:
print form.errors
else:
form = PageForm()
# made the change here
context_dict = {'form': form, 'category': cat, 'category_name_slug': category_name_slug}
return render(request, 'rango/add_page.html', context_dict)
FORMS -
from django import forms
from rango.models import Page, Category
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
# An inline class to provide additional information on the form.
class Meta:
# Provide an association between the ModelForm and a model
model = Category
fields = ('name',)
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of the page.")
url = forms.URLField(max_length=200, help_text="Please enter the URL of the page.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
class Meta:
# Provide an association between the ModelForm and a model
model = Page
# What fields do we want to include in our form?
# This way we don't need every field in the model present.
# Some fields may allow NULL values, so we may not want to include them...
# Here, we are hiding the foreign key.
# we can either exclude the category field from the form,
exclude = ('category',)
#or specify the fields to include (i.e. not include the category field)
#fields = ('title', 'url', 'views')
def clean(self):
cleaned_data = self.cleaned_data
url = cleaned_data.get('url')
# If url is not empty and doesn't start with 'http://', prepend 'http://'.
if url and not url.startswith('http://'):
url = 'http://' + url
cleaned_data['url'] = url
return cleaned_data
I think that is all the code necessary however just let me know if there are another files you would like to see and I'll put them up. Any help appreciated, thanks
A simple mistake, in my browser I was doing this 'http://127.0.0.1:8000/rango/category/other-frameworks/add_page.html', however my URL Tuple was not looking for a '.html' as the end so I removed it so it looked like this 'http://127.0.0.1:8000/rango/category/other-frameworks/add_page' and that solved my problem. Thanks to sgmart.