Django - serving an ical file - django

I'm server an icalender file through django_ical. Problem is that the file is named download.ics. I'm trying to change this to MyCalender.ics. If found this old snippet. I would prefer using django_ical, because it ingerates nicely with django syndication.
cal = vobject.iCalendar()
cal.add('method').value = 'PUBLISH' # IE/Outlook needs this
for event in event_list:
vevent = cal.add('vevent')
icalstream = cal.serialize()
response = HttpResponse(icalstream, mimetype='text/calendar')
response['Filename'] = 'filename.ics' # IE needs this
response['Content-Disposition'] = 'attachment; filename=filename.ics'

In django_ical the ICalFeed is inherited from django.contrib.syndication.views.Feed
In your app you inherit from ICalFeed to provide items, item_title and other methods that generate data for ics file.
You can override the __call__ method. The call to super will return you HttpResponse and you will add custom headers to it.
The code will be something like:
class EventFeed(ICalFeed):
"""
A simple event calender
"""
product_id = '-//example.com//Example//EN'
timezone = 'UTC'
def items(self):
return Event.objects.all().order_by('-start_datetime')
# your other fields
def __call__(self, request, *args, **kwargs):
response = super(EventFeed, self).__call__(request, *args, **kwargs)
if response.mimetype == 'text/calendar':
response['Filename'] = 'filename.ics' # IE needs this
response['Content-Disposition'] = 'attachment; filename=filename.ics'
return response
This code is not tested, so there might be some typos. Also you need to catch if there were errors in call to super. I do it by response.mimetype == 'text/calendar' but maybe there is a better way to do it

Related

The file is downloading in local but not in production server in Django Rest framework?

I have a simple API that downloads a file in the system. It works perfectly in the local server, but when I deploy the same code to production, it gives me a 500 server error. If I try with another id that doesn't exist, it will say the object is not found.
My code is as follows.
def get_permissions(self):
"""
Instantiates and returns the list of permissions that this view requires.
"""
if self.request.query_params.get("download") == "file":
return []
return [permission() for permission in self.permission_classes]
def get(self, request, *args, **kwargs):
if request.query_params.get("download") == "file":
return self.download_file(request)
/....other codes........./
def download_file(self, request, *args, **kwargs):
instance = self.get_object()
file_handle = instance.file.path
document = open(file_handle, "rb")
response = HttpResponse(FileWrapper(document), content_type="")
response["Content-Disposition"] = (
'attachment; filename="%s"' % instance.file_name
)
return response
My endpoint URL is this:
{{prod}}api/v1/example/notes/12/?download=file
When I call this api, putting local in place of prod, it works and I get a file downloaded, but not in production. Is it something to do with file being closed before getting a response??

Django REST framework, content negotiation

I am trying to get my endpoint to return a uri-list when asked for that and a json string as default. I am testing this in a unit test looking a bit like:
[...]
headers = {'Accept': 'text/uri-list'}
response = self.client.get('/api/v1/licenses/', headers=headers)
[...]
I have written a URIListRenderer like this:
from rest_framework import renderers
class URIListRenderer(renderers.BaseRenderer):
media_type = 'text/uri-list'
def render(self, data, media_type='None', renderer_context=None):
return "\n".join(data).encode()
Next I am trying to get my Response in my View to be rendered using my renderer:
class RestLicenses(APIView):
"""
List all licenses, or create a new license
"""
permission_classes = (IsAuthenticated,)
parser_classes = (MultiPartParser,)
renderer_classes = (JSONRenderer, URIListRenderer,)
def get(self, request, format=None,):
models = LicenseModel.objects.all()
if len(models) == 0 :
return Response('[]',status=204)
if request.META.get('headers') is not None :
if request.META.get('headers').get('Accept') == 'text/uri-list' :
result = [];
for m in models :
result.append(reverse('downloadLicense', args=[m.pk], request=request))
return Response(result, status=200)
serializer = LicenseJSONSerializer(request, models, many=True)
serializer.is_valid()
return HttpResponse(JSONRenderer().render(serializer.data), content_type='application/json', status=200)
But it seems impossible to get it to choose any other renderer than the first one in the list. How do I make it choose my URIListRenderer and not the json one?
Your unit test is not setting the headers correctly. As described here, you should use CGI style headers when using the Django test client:
response = self.client.get('/api/v1/licenses/', HTTP_ACCEPT='text/uri-list')
The content negotiation uses the real HTTP Accept header. In your code, you check that "headers" is set, but that's not the real HTTP Accept header. It should be:
if request.META.get('HTTP_ACCEPT') == "text/uri-list":
...
This is a block of code from the method finalize_response:
if isinstance(response, Response):
if not getattr(request, 'accepted_renderer', None):
neg = self.perform_content_negotiation(request, force=True)
request.accepted_renderer, request.accepted_media_type = neg
response.accepted_renderer = request.accepted_renderer
response.accepted_media_type = request.accepted_media_type
response.renderer_context = self.get_renderer_context()
As you can see, before it performs content negotiaton, it checks to see if the view has already set the renderer needed and if not tries to perform the negotiation by itself.
So you can do this in your get method:
if request.META.get('headers') is not None :
if request.META.get('headers').get('Accept') == 'text/uri-list' :
request.accepted_renderer = URIListRenderer
result = [];
for m in models :
result.append(reverse('downloadLicense', args=[m.pk], request=request))
return Response(result, status=200)
Another thing you should probably do is to place your custom renderer class before JSONRenderer in the renderer_classes list. In that way, it will first check the special case before the more general case during content negotiation. Is suspect that the request format also matches JSOnRender and it overshadows the custom renderer. Hope this helps

How to set Content-Disposition header using Django Rest Framework

I am serving an image using the Django REST framework. Unfortunately it downloads instead of displays. I guess I have to set the header Content-Disposition = 'inline'. How do I do this in the View or the Renderer?
class ImageRenderer(renderers.BaseRenderer):
media_type = 'image/*'
format = '*'
charset = None
render_style = 'binary'
def render(self, data, media_type=None, renderer_context=None):
return data
class ImageView(APIView):
renderer_classes = (ImageRenderer, )
def get(self, request, format=None):
image=MyImage.objects.get(id=1)
image_file = image.thumbnail_png.file
return Response(image)
According to this page in the Django docs, you can set the Content-Disposition header in this way:
response = Response(my_data, content_type='image/jpeg')
response['Content-Disposition'] = 'attachment; filename="foo.jpeg"'

Do I need to use request.is_ajax() in my views?

I am learning to use Ajax with Django, many tutorials simply check if request.method == 'GET' or POST. I am curious for what do we need .is_ajax() then. Is it normal no to use it or tutorials just show basic concepts?
I am curious for what do we need .is_ajax() then. Is it normal no to
use it or tutorials just show basic concepts?
Yes, it is totally normal not to use is_ajax. Most of the time what you care about in your views is the HTTP verb (e.g. GET, POST, PATCH..).
However there are certain cases where you want to know if the request is an AJAX request. Why? because you might want to return a different result depending if the request is ajax or not.
The most common use for this solution is PJAX. When you use a pjax technology, if the request is not an ajax request you render the entire page, whereas if the request comes from ajax you render only a partial of the page. Then the partial page is added in the correct place in the webpage by some sort of lib, such as https://github.com/defunkt/jquery-pjax.
For example, this is a mixing I wrote to use Pjax in django:
import os
from django.views.generic.base import TemplateResponseMixin
class PJAXResponseMixin(TemplateResponseMixin):
pjax_template_name = None
pjax_suffix = "pjax"
pjax_url = True
def get_context_data(self, **kwargs):
context = super(TemplateResponseMixin, self).get_context_data(**kwargs)
context['inner_template'] = self.pjax_template_name
return context
def get_template_names(self):
names = super(PJAXResponseMixin, self).get_template_names()
if self.request.is_ajax():
if self.pjax_template_name:
names = [self.pjax_template_name]
else:
names = self._pjaxify_template_var(names)
return names
def get(self, request, *args, **kwargs):
response = super(PJAXResponseMixin, self).get(request, *args, **kwargs)
if sel
f.pjax_url :
response['X-PJAX-URL'] = self.request.path
return response
def _pjaxify_template_var(self, template_var):
if isinstance(template_var, (list, tuple)):
template_var = type(template_var)(self._pjaxify_template_name(name) for name in template_var)
elif isinstance(template_var, basestring):
template_var = self._pjaxify_template_name(template_var)
return template_var
def _pjaxify_template_name(self, name):
container = self.request.META.get('HTTP_X_PJAX_CONTAINER', False)
if container is not False:
name = _add_suffix(name, clean_container_name(container))
return _add_suffix(name, self.pjax_suffix)
#################################################
# HELPER METHODS #
#################################################
def clean_container_name(name):
return name.replace('#', '')
def _add_suffix(name, suffix):
if "." in name:
file_name, file_extension = os.path.splitext(name)
name = "{0}-{1}{2}".format(file_name, suffix, file_extension)
else:
name += "-{0}".fomat(suffix)
return name
Basically, this mixing renders the default template if the request is not an ajax request. Whereas if the request is AJAX, it renders the pjax_template, if there is one, or the name of the default template prefixed with pjax.

pyPDF merging and displaying as httpresponse through django

I'm having trouble incorporating pyPDF logic to merge two pdf files into my django site. I have written code that works to merge files when run in a python file on the local server(but I need to explicitly identify which files to merge:
from pyPdf import PdfFileReader, PdfFileWriter
output = PdfFileWriter()
input1 = PdfFileReader(file("abc_form0.pdf", "rb"))
input2 = PdfFileReader(file("abc_form1.pdf", "rb"))
total_pages = input1.getNumPages()
total_pages1 = input2.getNumPages()
for page in xrange(total_pages):
output.addPage(input1.getPage(page))
for page in xrange(total_pages1):
output.addPage(input2.getPage(page))
outputStream = file("output.pdf", "wb")
output.write(outputStream)
outputStream.close()
This code (from my django site) works to display a single PDF. However, when more than one PDF is selected, only the final PDF selected displays, hence the need to integrate pyPDF to create one file containing all requested files.
class ABCAdmin(admin.ModelAdmin):
actions = ['print_selected_pdf']
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
response.write(obj.form)
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
I'm struggling with finding a way to combine these two methods. What I have tried is the following, but get an IO error [Errno 2] No such file or directory: 'obj.form'. So it is not reading in obj.form - I need to find a way for it to read in these objects correctly. Anyway, this is what I've tried.
def create_pdf(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
for obj in queryset:
output = PdfFileWriter()
input = PdfFileReader(file("obj.form","rb"))
total_pages = input.getNumPages()
for page in xrange(total_pages):
output.addPage(input.GetPage(page))
outputStream = file("output.pdf", "wb")
response.write(outputStream)
outputStream.close()
ABC.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
return response
def print_selected_pdf(self, request, queryset):
# prints the pdfs for those that are selected,
# regardless if the pdf_printed field is true or false
qs = queryset.filter(pdf_printed__exact=0)
return self.create_pdf(request, qs)
As always, thanks for any help or tips you may be able to provide!
I used stringIO in the output stream to get around this. I also had to define each page of the PDF form to be added to the output. This works to iterate for any number of forms that I need to be requested.
def create_form(self, request, queryset):
response = HttpResponse(mimetype="application/pdf")
response['Content-Disposition'] = 'attachment; filename=form.pdf'
output = PdfFileWriter()
for obj in queryset:
input = PdfFileReader(cStringIO.StringIO(obj.form))
output.addPage(input.getPage(0))
output.addPage(input.getPage(1))
output.addPage(input.getPage(2))
output.addPage(input.getPage(3))
ABC_Self.objects.filter(pk=obj.pk).update(user=request.user,pdf_printed="1",request_time=time.strftime("%H:%M:%S"),request_date=datetime.datetime.today())
outputStream = cStringIO.StringIO()
output.write(outputStream)
response.write(outputStream.getvalue())
return response
All you need to do is provide the full absolute path to your PDF files, e.g.
/home/joseph/form.pdf
or
c:/home/joseph/form.pdf
etc.