Trouble downloading file using Django FileTransfers - python

I have am uploading a file through the admin section of my site that I would like to be able to be publicly downloaded through my website. I know that the file I have uploaded has been successfully uploaded because I can view it in the App Engine Blob storage. I am having trouble finding out what isn't working with the code below:
relevant part of my modeL:
class CalendarEvent (models.Model):
file = models.FileField(upload_to='uploads/%Y/%m/%d/%H/%M/%S/')
in my views.py file the relevant code is:
def calendar(request):
events = CalendarEvent.objects.exclude(start__lt=datetime.datetime.now()).order_by('start')
return render_to_response('home/calendar.html',{'events': events},context_instance=RequestContext(request))
def download_handler(request, pk):
upload = get_object_or_404(CalendarEvent, pk=pk)
return serve_file(request, upload.file, save_as=True)
in my template the relevant code is:
{% for e in events %}
{% url Calendar.views.download_handler pk=e.pk as fallback_url %}
Download
{% endfor %}

Your view name for your download_handler should be <appname>.views.download_handler. You probably don't have an app named "e" with an appropriate view in it.

Related

Change CSS stles from view in Django

Sorry if this is an obvious question, I am new to django and still learning.
I am creating a website that contains 6 images in total. The images in the website should remain invisible until its image id is passed from views.py.
I have a template index.html page and view that loads when this url is accessed localhost/imageid
What I need now is to make the image visible whenever its url is access. So for instance if a user goes to localhost/1. QR code 1 should be made visible. I am also storing the state of all access images. So if the user accesses the website again and goes to localhost/2 it should make image 1 and 2 visible. I am using sessions to store the state. I just need a way of making the images visible.
Thankyouuuu
Depends so a basic view would be:
from django.views.generic import TemplateView
class MyView(TemplateView):
template_name = 'my_html.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['img_id'] = self.kwargs.get('pk') # this is whatever the variable is named in the URL.
return context
html would be something like this.:
<body>
{%if img_id == 1 %}
<img src="image1path">
{%elif img_id == 2 %}
<img src="image2path">
{% endif %}
</body>
A better way would be to store the images in a database and get the image paths from a model, then you would get that in the context and reference it in the template instead of having if statements.

Django partially restricting contents according to different user group

Just started Django last week and I am facing this problem regarding two types of users (normal and VIP) and how to restrict normal users from viewing VIP contents.
I am using Django with MySQL. My site does not requires user to signup but instead, I will be giving them username/password. I manually creates user via Django admin and group them according to VIP or normal. I will be uploading contents (again via admin) and I have a Boolean check-box that determine whether it is a VIP content or not.
What I done so far:
User must login to view the contents
Upon logging in, VIPs can see VIP contents and normal users will not be able to see VIP contents.
FAQ.html
{% extends 'base.html' %}
...skipping...
{% for faqs in fq reversed %}
{{ faqs.name }}
{{ endfor }}
faq_topics.html
<h2>{{ faqs.name }}</h2>
<h6 style="padding: 20px 20px 20px 20px">{{ faqs.description|linebreaksbr }}</h6>
urls.py
urlpatterns = [
path('faq/', views.faq, name='faq'),
url(r'^faq/(?P<pk>\d+)/$', views.faq_topics, name='faq_topics'),]
views.py
def is_user(user):
return user.groups.filter(name='User').exists()
def is_vip(user):
return user.groups.filter(name='VIP').exists()
#login_required
#user_passes_test(is_user)
def faq(request):
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
models.py
class FAQ(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=300)
vip_only = models.BooleanField(default=False)
Error Case
3 FAQ entries are made, the first one is classified as VIP only.
When a normal user log in into FAQ section, he/she will only see 2 contents
Welcome to FAQ section
127.0.0.1:8000/faq/3/
127.0.0.1:8000/faq/2/
However, the user can easily guess that faq/1/ exists and when they actually tries, they can have access to vip contents too.
I have been searching the web for the past 2 days, I learn how to user #user_passes_test to restrict users and objects.filter(vip_only=False) to restrict contents.
Additionally, I wish to have my views.py controlling the contents for both kind of user but I have yet to implement that yet. Will be very grateful if someone can teach me how to implement "2 views in one templates".
I have read from,
show different content based on logged in user django
django - 2 views in one template
My solution for splitting user
#login_required
def faq(request):
'''user is separate using groups'''
if request.user.is_superuser or request.user.groups.filter(name='VIP').exists():
fq = FAQ.objects.all()
else:
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
You should do something like this
def faq(request):
if request.user.is_superuser:
fq = FAQ.objects.all()
else:
fq = FAQ.objects.filter(vip_only=False)
return render(request, 'faq.html', {'fq':fq})
You can also use similar approach for doing things in the template:
{% if user.is_superuser %}
#...show what you only want to show the superuser
{% endif %}
And in your method for the single faq, do something like:
def faq_topics(request, pk):
faq_item=FAQ.objects.get(id=pk)
if not request.user.is_superuser and not request.user.groups.filter(name='VIP').exists():
if fqa_item.vip_only:
return render(request, 'path_to_access_error_template', {'fq':faq_nr})
return render(request, 'faq_item.html', {'fq':faq_nr})

Display a picture via Django saved on mysql database

I want to display a picture with Django, which is already saved on a mysql database. In all Methods I found people used models.Imagefield(upload_to='path'), but I think this will save the file into this directory and people from other computers won´t have access later.
So the question is, how do I access the database directly and display the picture without saving it in between?
I already managed to do this in python, but I am not quite sure how to implement the code into the models.py.
Something like this was my approach :
class mysqlpicture(models.Model):
#mysqlpic=something?
def openpic():
connection = pymysql.connect(user='root', passwd='***',
host='localhost',
database='db')
cursor = connection.cursor()
sql1='select * from Pictures'
cursor.execute(sql1)
data=cursor.fetchall()
mysqlpic=io.BytesIO(data[1][0])#one means second element of column zero
#img=Image.open(mysqlpic)
#img.show()
cursor.close()
return mysqlpic
and then I tried in the views.py to give mysqlpicdirectly to the httpresponse like this:
def MysqlView(request):
query_results = mysqlpicture.objects.all()
template = loader.get_template('polls/pic.html')
context = {
'query_results': query_results,
}
return HttpResponse(template.render(context, request))
using a template pic.htmlto insert a picture like:
{% for n in query_results %}
<mysqlpic src="{{ n.mysqlpic.url }}" />
{% endfor %}
Has someone an idea how to to this in the right way?
Yes, using ImageField does allow people from other computers to access it later. It inherits all attributes and methods from FileField which uploads the file into the server in the path specified in upload_to and also saves this path into your database to keep its reference. All of that happens when you call the save() method in your model instance.
Then, you can create the url to build a link by calling your_instance.image_field.url in your template. Also remember to have a look at MEDIA_ROOT and MEDIA_URL which respectively tells Django the server root path for saving your files and the url that handles the media served from MEDIA_ROOT.
You can use BinaryField for this purposes.
But as stated in the django documentation:
Although you might think about storing files in the database, consider that it is bad design in 99% of the cases.
from django.db import models
class MysqlPicture(models.Model):
mysqlpic = models.BinaryField()
# ...
And then to display in the template you can convert binary data to base64:
from base64 import b64encode
def mysql_view(request):
query_results = MysqlPicture.objects.all()
template = loader.get_template('polls/pic.html')
context = {
'images': [b64encode(obj.mysqlpic) for obj in query_results],
}
return HttpResponse(template.render(context, request))
Template:
{% for image in images %}
<img src="data:;base64,{{ image }}">
{% endfor %}

Django pass render_to_response template in other template

this is probably a question for absolute beginners since i'm fairly new to progrmaming. I've searched for couple of hours for an adequate solution, i don't know what else to do.
Following problem. I want to have a view that displays. e.g. the 5 latest entries & 5 newest to my database (just an example)
#views.py
import core.models as coremodels
class LandingView(TemplateView):
template_name = "base/index.html"
def index_filtered(request):
last_ones = coremodels.Startup.objects.all().order_by('-id')[:5]
first_ones = coremodels.Startup.objects.all().order_by('id')[:5]
return render_to_response("base/index.html",
{'last_ones': last_ones, 'first_ones' : first_ones})
Index.html shows the HTML content but not the content of the loop
#index.html
<div class="col-md-6">
<p> Chosen Items negative:</p>
{% for startup in last_ones %}
<li><p>{{ startup.title }}</p></li>
{% endfor %}
</div>
<div class="col-md-6">
<p> Chosen Items positive:</p>
{% for startup in first_ones %}
<li><p>{{ startup.title }}</p></li>
{% endfor %}
Here my problem:
How can I get the for loop to render the specific content?
I think Django show render_to_response in template comes very close to my problem, but i don't see a valid solution there.
Thank you for your help.
Chris
--
I edited my code and problem description based on the solutions provided in this thread
the call render_to_response("base/showlatest.html"... renders base/showlatest.html, not index.html.
The view responsible for rendering index.html should pass all data (last_ones and first_ones) to it.
Once you have included the template into index.html
{% include /base/showlatest.html %}
Change the view above (or create a new one or modify the existing, changing urls.py accordingly) to pass the data to it
return render_to_response("index.html",
{'last_ones': last_ones, 'first_ones' : first_ones})
The concept is that the view renders a certain template (index.html), which becomes the html page returned to the client browser.
That one is the template that should receive a certain context (data), so that it can include other reusable pieces (e.g. showlatest.html) and render them correctly.
The include command just copies the content of the specified template (showlatest.html) within the present one (index.html), as if it were typed in and part of it.
So you need to call render_to_response and pass it your data (last_ones and first_ones) in every view that is responsible for rendering a template that includes showlatest.html
Sorry for the twisted wording, some things are easier done than explained.
:)
UPDATE
Your last edit clarified you are using CBV's (Class Based Views).
Then your view should be something along the line:
class LandingView(TemplateView):
template_name = "base/index.html"
def get_context_data(self, **kwargs):
context = super(LandingView, self).get_context_data(**kwargs)
context['last_ones'] = coremodels.Startup.objects.all().order_by('-id')[:5]
context['first_ones'] = coremodels.Startup.objects.all().order_by('id')[:5]
return context
Note: personally I would avoid relying on the id set by the DB to order the records.
Instead, if you can alter the model, add a field to mark when it was created. For example
class Startup(models.Model):
...
created_on = models.DateTimeField(auto_now_add=True, editable=False)
then in your view the query can become
def get_context_data(self, **kwargs):
context = super(LandingView, self).get_context_data(**kwargs)
qs = coremodels.Startup.objects.all().order_by('created_on')
context['first_ones'] = qs[:5]
context['last_ones'] = qs[-5:]
return context

Uploading files in django admin, download through public site

I am running a Django website and I want to be able to upload a file through my admin panel and then have visitors to the main site be able to download it. I am running my site using Django-nonrel, Django FileTransfers and Google App Engine. I believe the upload functionality is working correctly as I am able to see the file in my App Engine Blob Storage. What I can't seem to figure out is how to present a download link to the specified file on the public website. I have pasted the relevant classes below:
I have an app called Calendar, that has the following model:
class CalendarEvent (models.Model):
start = models.DateTimeField(auto_now=False, auto_now_add=False)
end = models.DateTimeField(auto_now=False, auto_now_add=False)
title = models.CharField(max_length=500)
description = models.TextField()
file = models.FileField(upload_to='uploads/%Y/%m/%d/%H/%M/%S/')
Here is the view:
def calendar(request):
events = CalendarEvent.objects.exclude(start__lt=datetime.datetime.now()).order_by('start')
return render_to_response('home/calendar.html',{'events': events},context_instance=RequestContext(request))
def download_handler(request, pk):
upload = get_object_or_404(CalendarEvent, pk=pk)
return serve_file(request, upload.file, save_as=True)
Here is my admin:
class calendarAdmin(admin.ModelAdmin):
list_display = ('title','start','end')
admin.site.register(CalendarEvent, calendarAdmin)
Finally, here is the relevant part of my template:
{% for e in events %}
{% url Calendar.views.download_handler pk=e.pk as fallback_url %}
Download
{% endfor %}
{% firstof e.file|public_download_url fallback_url %} is just returning blank, i'm not sure where I am going wrong.
The GAE blob store does not support public download according to the documentation here, so if you use the default backend for public download urls, it returns None. So my guess is that e.file|public_download_url always return None. You could verify that.
Then I think your template is wrong. You're trying to access e.views.download_handler where it should be Calendar.views.download_handler if your app is named Calendar.
I think the sample on the django-filetransfers page is error prone because the variable used in the template loop has the same name as the sample app: "upload".
If this doesn't fix it, could you post your urls.py from Calendar app. It could be that the template's url method is not able to resolve the url for Calendar.views.download_handler if there is no mapping in urlpatterns.
You should have something like
urlpatterns = patterns('Calendar.views',
...
(r'^download/(?P<pk>.+)$', 'download_handler'),
...
)
in this file.
I don't see anything special, eg
Download File
should work, or just use e.file.url directly?
I haven't deployed on Google App Engine myself, but this appears to be what django-filetransfers was designed for:
http://www.allbuttonspressed.com/projects/django-filetransfers#handling-downloads
edit: I believe I've answered this in the other question you posted, then: Trouble downlaoding file using Django FileTransfers
I think easiest way is to write a view since this file blob cannot be retrieved directly to write a function which is such:
def file_transfer(req, calendar_event_id):
try:
ce = CalendarEvent.objects.get(calendar_event_id)
except CalendarEvent.DoesNotExist:
raise Http404()
file = ce.file (Write Google appengine specfic routine to pull file)
return HttpResponse(file, media_type='file content type')
Hook it up on urls.py

Categories