Django CBV : Download PDF document from media - django

I would like to download pdf file in my view but I don't overcome to display browser window which let to download my file.
The downloadPDF function seems to work well but nothing appears on my browser.
This is my class :
class TokenDownloadView(TemplateView):
template_name = 'app/token.html'
def get_context_data(self, **kwargs):
now = timezone.now()
context = super().get_context_data(**kwargs)
context['token'] = self.kwargs['token']
token = context['token']
download = Download.objects.get(token__iexact=token)
upload_doc = Document.objects.get(id=download.pub_id).upload
if download and download.expiration_date > now:
print("token valide jusqu'à : " + str(download.expiration_date))
print("il est actuellement : " + str(now))
print(' ==> Token existe et valide <==')
messages.success(self.request, 'Vous allez télécharger le document')
self.downloadPDF(upload_doc)
if download and download.expiration_date < now:
print("token valide jusqu'à : " + str(download.expiration_date))
print("il est actuellement : " + str(now))
print('==> Token existe mais a expiré <==')
messages.error(self.request, 'Document non téléchargé : la session a expiré')
return context
def downloadPDF(self, upload_doc):
from django.core.files.storage import FileSystemStorage
from django.http import HttpResponse, HttpResponseNotFound
fs = FileSystemStorage()
filename = upload_doc
if fs.exists(filename):
with fs.open(filename) as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="mypdf.pdf"'
return response
else:
return HttpResponseNotFound('The requested pdf was not found in our server.')
I miss something in my class which could let to download my pdf ?
EDIT :
I edited my class in order to convert get_context_data() to get() method. It seems to work but I would like to get your mind :
class TokenDownloadView(TemplateView):
template_name = 'app/token.html'
def get(self, request, *args, **kwargs):
now = timezone.now()
context = super().get(request, *args, **kwargs)
token = self.kwargs['token']
download = Download.objects.get(token__iexact=token)
document_title = Document.objects.get(id=download.pub_id).title
upload_doc = Document.objects.get(id=download.pub_id).upload
if download and download.expiration_date > now:
print("Token is valid until : " + str(download.expiration_date))
print("Now : " + str(now))
print(' ==> Token exists and valid <==')
messages.success(self.request, 'You are going to download document(s)')
resp = self.downloadPDF(upload_doc, document_title)
if download and download.expiration_date < now:
print("Token is valid until : " + str(download.expiration_date))
print("Now : " + str(now))
print('==> Token exists but has expired <==')
messages.error(self.request, 'Session of 10 minutes has expired - Please download document(s) one more time')
return render(request, self.template_name)
return resp
def downloadPDF(self, upload_doc, document_title):
fs = FileSystemStorage()
filename = upload_doc
if fs.exists(filename):
with fs.open(filename) as pdf:
response = HttpResponse(pdf, content_type='application/pdf')
response['Content-Disposition'] = 'attachment; filename="%s.pdf"' % document_title
return response
else:
return HttpResponseNotFound('The requested pdf was not found in our server.')

You call the downloadPDF but you ignore its return value. You would need to return the result of calling that method. However, this will not work because you cannot return a response from get_context_data; that method, as the name implies, must return a dictionary context and not a response.
You need to move this code into the get method.

Related

Django form class and view class connected

Hi in my code(not written by me) i have django form class and views class. I dont know how this is connected each other. Can anyone tell me how this is connected? Also can any one please tell me how this messege : Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using " "credentials may result in collecting being rate limited by the " "social media API is displayed, i mean if i need to change the alignment of this text where i should change?
My code classes are :
from forms.py :
class CollectionTwitterSearch2Form(BaseCollectionForm):
incremental = forms.BooleanField(initial=True, required=False, label=INCREMENTAL_LABEL, help_text=INCREMENTAL_HELP)
def __init__(self, *args, **kwargs):
super(CollectionTwitterSearch2Form, self).__init__(*args, **kwargs)
self.helper.layout[0][5].extend(('incremental',))
if self.instance and self.instance.harvest_options:
harvest_options = json.loads(self.instance.harvest_options)
if "incremental" in harvest_options:
self.fields['incremental'].initial = harvest_options["incremental"]
def save(self, commit=True):
m = super(CollectionTwitterSearch2Form, self).save(commit=False)
m.harvest_type = Collection.TWITTER_SEARCH_2
harvest_options = {
"incremental": self.cleaned_data["incremental"],
}
m.harvest_options = json.dumps(harvest_options, sort_keys=True)
m.save()
return m
from views.py :
def _get_credential_use_map(credentials, harvest_type):
credential_use_map = {}
if harvest_type in Collection.RATE_LIMITED_HARVEST_TYPES:
for credential in credentials:
active_collections = 0
inactive_collections = 0
for collection in credential.collections.all():
if collection.is_on:
active_collections += 1
else:
inactive_collections += 1
if active_collections == 0 and inactive_collections == 0:
credential_use_map[credential.id] = ("", "")
else:
credential_use_map[credential.id] = ("warning",
"Credential is in use by {0} collections that are turned on and "
"{1} collections that are turned off. Be mindful that over-using "
"credentials may result in collecting being rate limited by the "
"social media API.".format(active_collections,
inactive_collections))
return credential_use_map
class CollectionCreateView(LoginRequiredMixin, CollectionSetOrSuperuserPermissionMixin, SuccessMessageMixin,
CreateView):
model = Collection
template_name = 'ui/collection_create.html'
def get_initial(self):
initial = super(CollectionCreateView, self).get_initial()
initial["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
return initial
def get_context_data(self, **kwargs):
context = super(CollectionCreateView, self).get_context_data(**kwargs)
context["collection_set"] = CollectionSet.objects.get(pk=self.kwargs["collection_set_pk"])
harvest_type = self.kwargs["harvest_type"]
context["harvest_type_name"] = _get_harvest_type_name(harvest_type)
credentials = _get_credential_list(self.kwargs["collection_set_pk"], harvest_type)
context["credentials"] = credentials
context["credential_use_map"] = _get_credential_use_map(credentials, harvest_type)
context["platform"] = Collection.HARVEST_TYPES_TO_PLATFORM[self.kwargs["harvest_type"]]
return context
def get_form_kwargs(self):
kwargs = super(CollectionCreateView, self).get_form_kwargs()
kwargs["coll"] = self.kwargs["collection_set_pk"]
kwargs['credential_list'] = _get_credential_list(self.kwargs["collection_set_pk"], self.kwargs["harvest_type"])
return kwargs
def get_form_class(self):
return getattr(forms, _get_collection_form_class(self.kwargs["harvest_type"]))
def get_success_url(self):
return reverse('collection_detail', args=(self.object.pk,))
def get_success_message(self, cleaned_data):
if self.object.required_seed_count() != 0:
return "New collection added. You can now add seeds."
return "New collection added."
Full code is here in this git : https://github.com/gwu-libraries/sfm-ui/tree/master/sfm/ui
It would be great anyone can explain how these two classes and template is connected and how the messege is displayed
The CollectionCreateView class is conected to the Form using the function get_form_class, this function is called by default by the CreateView, in there you can see is calling _get_collection_form_class() and as an argument is passing self.kwargs['harvest_type'] this kwargs is comming from the url declaration. The _get_collection_form_class function is returning the CollectionTwitterSearch2Form when the harvest_type is something like TwitterSearch2. The template is given by the template_name = 'ui/collection_create.html' again this is the default vehaviour. And finally for the message this is using SuccessMessageMixin.

Testing views in Django, post requests. When I query my Workspace.objects.all() it never exists

Whenever I query my Workspace.objects.all() it never exists in my tests despite working fine on the server.
def create_workspace(request, primary_key):
if request.method == 'POST':
messages.success(request, "New workspace was successfully created!")
form = Workspace_Form(request.POST)
if form.is_valid():
user = User.objects.get(id=primary_key)
workspace = Workspace()
workspace.user = user
workspace.name = form.cleaned_data.get('name')
workspace.description = form.cleaned_data.get('description')
workspace.save()
messages.success(request, "New workspace " + workspace.name + " was successfully created!")
return redirect('core:user_page', primary_key)
else:
messages.error(request, "Workspace must have a name!")
return redirect('core:user_page', primary_key)
class Test_Create_Workspace(TestCase):
def setUp(self):
self.user_page_url = reverse('user_page',kwargs={'primary_key':1})
self.user = User.objects.create(username='testuser')
self.user.set_password('12345Bunny')
self.user.save()
self.group = Group(name = 'customer')
self.group.save()
self.group.user_set.add(self.user)
self.group.save()
client = Client()
client.login(username='testuser', password='12345Bunny')
return super().setUp()
def tearDown(self) -> None:
self.user.delete()
return super().tearDown()
def test_can_create_valid_workspace(self):
self.workspace={
'name' : 'workspace name',
'description' : 'h',
}
response = self.client.post(reverse('core:create_workspace',kwargs={'primary_key':1}),self.workspace)
Workspace.objects.all().refresh_from_db()
messages = list(get_messages(response.wsgi_request))
self.assertEqual(len(messages), 1)
self.assertEqual(str(messages[0]), "New workspace " + self.workspace.name + " was successfully created!")
self.assertEqual(response.status_code,302)
self.assertTrue(Workspace.objects.count(), 1)

download a file after rendering a template in django

I am trying to download a csv file and rendering a template at the same time after redirecting from a view but it only downloads the file ,doesn't render the template.
I think it can be handled by middleware but finding an easier solution to it.
class ProductUploadView(FormView):
template_name ='upload.html'
form_class = csvform
success_url = 'response/'
failed_rows = []
def post(self,request,*args,**kwargs):
form = self.form_class(request.POST,request.FILES)
file = request.FILES['csvfile']
csvfile = csv.reader(utf_8_encoder(file), dialect='excel', delimiter=',')
csvfile.next()
for row in csvfile:
if row[1] in ['PRES','GEN','GEN'] and row[2] in ['CAPS','TAB','SYP','GEL','SUSP','INJ']:
try:
p, created = Product.objects.get_or_create(brand_name=row[0], category=row[1], defaults={'form': row[2], 'mrp': row[3], 'ptr': row[4]})
except ValidationError:
self.failed_rows.append(row)
pass
else:
self.failed_rows.append(row)
return HttpResponseRedirect(self.success_url)
class UploadedView(ProductUploadView):
template_name = 'response.html'
rows = ProductUploadView.failed_rows
def download_view(self,request):
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename="somefilename.csv"'
writer = csv.writer(response)
writer.writerows(self.rows)
return response
def get(self,request):
response = TemplateResponse(request,self.template_name,{'num':len(self.rows),'failed_rows':self.rows})
response.add_post_render_callback(download_view)
return response

django test ValueError: the view didn't return an HttpResponse object. It returned None instead

I have view to submit data. From the web browser, i can submit data and redirected nicely. But on the test, i got the error: didn't return an HttpResponse object. It returned None instead.
How can i pass my test?
The view code is:
def insert_barang_po(request, po_id=None):
"submit barang2 dalam PO, ditamilkan setelah insert PO"
po = get_object_or_404(PurchaseOrder, id=po_id)
context = {'menu_sales': True,}
context['po'] = {'id': po.id,}
if request.method == 'POST':
form = InsertBarangPOForm(request.POST)
if form.is_valid():
barang_po = BarangPurchaseOrder.objects.create(
po=form.cleaned_data['po'],
finish_good=form.cleaned_data['finish_good'],
jumlah_barang=form.cleaned_data['jumlah_barang'])
return redirect(reverse('sales:insert_barang_po', args=[po.id]))
else:
form = InsertBarangPOForm(initial={'po':po,})
context['form'] = form
return render(request, 'sales/insert_barang_po.html', context)
The model code is:
class BarangPurchaseOrder(models.Model):
"Menyimpan isi dari setiap PO yg diterima"
po = models.ForeignKey(PurchaseOrder)
finish_good = models.ForeignKey(FinishGood)
jumlah_barang = models.IntegerField()
def __unicode__(self):
return self.finish_good.nama
The test code is
def test_insert_barang_po_valid_po_id(self):
po = PurchaseOrder.objects.create(nomor_po='zaneti')
fg = FinishGood.objects.create(nama='selesai', code='sls')
c = Client()
response = c.post(reverse('sales:insert_barang_po', args=[po.id]),
{'po': po,
'finish_good': fg.id,
'jumlah_barang': '35',},
follow=True)
bpo = BarangPurchaseOrder.objects.filter(finish_good=fg) \
.order_by('id').reverse()
self.assertEqual(bpo[0].jumlah_barang, 35)
Your views.py doesn't cover all the cases. If the form is invalid, your method simply returns nothing, that's why you have this error message. You should have an else statement corresponding to if form.is_valid() statement.

How do i pass a form data as context?

I have this PDFview class now i want to use it in any form template so i can see preview of that form data before sending data.
class PDFView(View):
def get(self, request, **kwargs):
template = get_template("pim/pdfview.html")
ctx = {}
ctx["test"] = Employee.objects.all()
context = Context(ctx)
html = template.render(context)
result = StringIO.StringIO()
pdf = pisa.pisaDocument(StringIO.StringIO(html.encode("ISO-8859-1")), result)
if not pdf.err:
response = http.HttpResponse(result.getvalue(), mimetype='application/pdf')
response['Content-Disposition'] = 'attachment; filename="leavereport.pdf"'
return response
return http.HttpResponse('We had some errors<pre>%s</pre>' % cgi.escape(html))