How can I get the page title into a Django HttpResponse object - django-views

I have a Django view that displays a pdf file
# views.py
def get(self, request, recipe_pk):
"""Display a recipe as a pdf."""
recipe = Recipe.objects.get(pk=recipe_pk)
recipe_file_path = _get_recipe_path(recipe.file_name.name)
with open(recipe_file_path, 'rb+') as f_recipe:
response = HttpResponse(f_recipe.read(), content_type='application/pdf')
return response
pdf.closed
This works fine but the recipe_pk appears in the page title
I have got the recipe record in the view and would like too use recipe.name as the title
Is there any way I can send the more friendly title?

Thanks to #Willem Van Onsem I have solved it.
for a PDF file the browser will look for the title of that pdf file (the "metadata"). So if that is missing, it falls back to the URL
Instead of passing the recipe.pk in the url, I pass recipe.name
def get(self, request, recipe_name):
"""Display a recipe as a pdf."""
recipe = Recipe.objects.get(name=recipe_name)
recipe_file_path = _get_recipe_path(recipe.file_name.name)
with open(recipe_file_path, 'rb+') as f_recipe:
response = HttpResponse(f_recipe.read(), content_type='application/pdf')
return response
Perfect!

Related

"The submitted data was not a file. Check the encoding type on the form." validation error, despite uploading correct file (django rest framework)

I'm trying to create endpoint for uploading images, in my api, which i'm building with django rest framework.
When I try to test the endpoint with postman, i'm getting response
"image": [
"The submitted data was not a file. Check the encoding type on the form."
]
with status code 400.
When I try to print variable with image to console I get
[<TemporaryUploadedFile: test.jpg (image/jpeg)>]
I've checked out some tutorials and I think i'm sending the file correctly.
That is my post man configuration
that's the view
class ImageView(APIView):
parser_classes = (MultiPartParser, )
permission_classes = (IsAuthenticated, IsRestaurant)
def post(self, request, *args, **kwargs):
data = {
'image': request.data.pop('image'),
'info': request.user.info.pk
}
file_serializer = RestaurantImageSerializer(data=data)
if file_serializer.is_valid():
file_serializer.save()
return Response(status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
the serializer
class RestaurantImageSerializer(serializers.ModelSerializer):
class Meta:
model = RestaurantImage
fields = '__all__'
and model
class RestaurantImage(models.Model):
info = models.ForeignKey(RestaurantInfo, related_name='images', on_delete=models.CASCADE)
image = models.ImageField()
def __str__(self):
return self.image.name
Your Postman configuration could be an issue, try removing all the wrong or unnecessary headers. I think you need only Authorization in your case. This also could help you out: https://stackoverflow.com/a/41435972/4907382

Django REST testing - how to specify pk argument referring to the model instance created in setUp function

I have a model "Article" and I want to test if authorized user can GET an individual article.
The testing class is:
class TestPost(APITestCase):
def setUp(self):
self.factory = APIRequestFactory()
self.user = User.objects.create_user(
username='Name', email='test#company.com', password='secret')
self.article = Article.objects.create(
author = 'Author', title = 'Article title', body = 'Body content ...')
def test_detail_user(self):
request = self.factory.get(reverse('article_id', kwargs={'pk': 1}))
request.user = self.user
response = ArticleDetail.as_view()(request, pk=1)
self.assertEqual(response.status_code, 200,
f'Expected Response Code 200 - OK, received {response.status_code} instead.')
The URL pattern is:
path('<int:pk>/', ArticleDetail.as_view(), name = 'article_id'),
And when running tests I get the following error:
f'Expected Response Code 200 - OK, received {response.status_code} instead.')
AssertionError: 404 != 200 : Expected Response Code 200 - OK, received 404 instead.
I suppose the problem is in the specified 'pk', but I cannot figure out how to specify pk without stating an exact figure of 1. How can I refer to the article created in setUp function instead?
I may be misunderstanding, but you should be able to reference it by simply doing something like:
def test_detail_user(self):
article_id = self.article.pk
...
# the rest of your code here using article_id as the id of
# the article you are retrieving

Django: Adding attachment to generic.DetailView

I have a existing Django App with a detailview for UserProfiles. Now our client wants to have the ability to 'download' the information of the page to a PDF.
I have added a button in the HTML to trigger the 'generate-attachement' method
<div class="input-group">
<button name='zip' value="True" type="submit">Get report</button>
</div>
I have also added a 'generate_pdf' method to the view, which is triggered by the button above.
class ProfileView(ProfileMixin, generic.DetailView):
template_name = 'user/profile/detail.html'
def get_object(self, queryset=None):
return self.context.profile_user
def generate_pdf(self):
from reportlab.pdfgen import canvas
response = HttpResponse(content_type='application/pdf')
response['pdf'] = 'attachment; filename="summary.pdf"'
p = canvas.Canvas(response)
p.drawString(100, 100, "Hello world.")
p.showPage()
p.save()
print(p)
return response
def get_context_data(self, **kwargs):
data = super(ProfileView, self).get_context_data(**kwargs)
#Check if 'get attachement' button has been pressed
if self.request.GET.get('zip', None):
self.generate_pdf()
#Code to load relevant data form a large number of models
#append it to the 'data' variable .
#(e.g data['group_year] = ...etc
return data
However, when I run this code / press the button the method / print commands are all triggered, but no attachment is returned to the browser
<reportlab.pdfgen.canvas.Canvas instance at 0x112165638>
[08/Feb/2018 12:30:08] "GET /user/profile/459/?zip=True HTTP/1.1" 200 41749
Yet I got most of the code from the official Django Documentation, so its not entirely clear to me why my code is failing.
Does anyone have an idea of what I am doing wrong?
You need to override get() method of your view to customize response:
class ProfileView(ProfileMixin, generic.DetailView):
template_name = 'user/profile/detail.html'
def get(self, request, *args, **kwargs):
#Check if 'get attachement' button has been pressed
if self.request.GET.get('zip', None):
return self.generate_pdf()
return super(ProfileView, self).get(request, *args, **kwargs)
looks like you should have used 'Content-Disposition' rather than 'pdf' to add your file to the response.
response['Content-Disposition'] = 'attachment; filename="summary.pdf"'

Filedownload Issue Django - File becomes practically empty

I am having the following issue with filedownload from product views in Django product site:
The issue: Filesize of downloaded file is practically 1kb whilst it should be a normal image filesize (20kb in my example).
So the to-download file is present in static folder of the product instance.id (static_cdn/protected/instance.id/image.jpg -- context: product site where user can upload a file to the corresponding product view).
However, whenever I try to download it from the product view, it downloads the file with the right filename (including the added instance.id number before the filename), but the filesize is almost null. I think it has to do the with the class ProductDownloadView.
Please find the relevant codes below:
views.py:
class ProductDownloadView(MultiSlugMixin, DetailView):
model = Product
def get(self, request, *args, **kwargs):
obj = self.get_object()
filepath = os.path.join(settings.PROTECTED_ROOT, obj.media.path)
response = HttpResponse(file(filepath), content_type="application/force-download")
response["Content-Disposition"] = "attachment;filename=%s" % (obj.media.name)
response["X-SendFile"] = str(obj.media.name)
return response
models.py
class Product(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True)
managers = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name="managers_product")
media = models.FileField(blank=True,
null=True,
upload_to=download_media_location,
storage=FileSystemStorage(location=settings.PROTECTED_ROOT))
def __unicode__(self):
return self.title
def get_absolute_url(self):
view_name = "products:detail_slug"
return reverse(view_name, kwargs={"slug": self.slug})
def get_download(self):
view_name = "products:download_slug"
url = reverse(view_name, kwargs={"slug": self.slug})
return url
Please find below the printed obj, filepath and response variables:
print obj:
pr8
print filepath:
C:\Users\xx\xx\xx\market_place\static_cdn\protected\8\Beach.jpg
print response:
Content-Type: application/force-download
Content-Disposition: attachment;filename=8/Beach.jpg
X-SendFile: 8/Beach.jpg
���� JFIF �� C
[21/Jun/2017 02:17:05] "GET /products/pr8/download/ HTTP/1.1" 200 52
I think I've found the answer. I got the file-download to work by using the open method instead of the file method. Because of this solution I am deviating from a tutorial, but at least I got the job done.
So I got it working by changing the following rule:
response = HttpResponse(file(filepath), content_type="application/force-download")
into:
response = HttpResponse(open(filepath, "rb"), content_type="application/force-download")
So basically adding a mode to the function. Even the file method works after adding the mode "rb".
try this:
response = HttpResponse(content_type="image/jpeg")
response['X-Sendfile'] = obj.media.path
response['Content-Disposition'] = 'attachment; filename=%s' % smart_str(photo.image.name)

How do you create a view that returns the next object in a given list? code included

I have a view that displays object information when the correct URL is provided however I would like to create a similar view that returns url information from the record that is located next.
So in essence take the info from my first view, list/order it in some way preferably by date and return the records located next and previous in the list.
Can somebody please explain to me and provide a simple example of how I would do this. I am new to django and I am stuck. All Help is greatly appreciated. Thanks.
Here are the models URLs and views I have used
Model
class Body(models.Model):
type = models.ForeignKey(Content)
url = models.SlugField(unique=True, help_text='')
published = models.DateTimeField(default=datetime.now)
maintxt = models.CharField(max_length=200)
View
def news_view(request, url):
news = get_object_or_404(Body, url=url)
next = news.get_next_by_published().get_absolute_url()
return render_to_response('news/news_view.html', {
'news': news,
'next': next
}, context_instance=RequestContext(request))
URL
url(r'^/(?P<url>[\w\-]+)/$', 'news_view', name="news_view"),
Template
Next</p>
add get_absolute_url to your model
class Body:
#[....]
def get_absolute_url(self):
return "/%i/" % self.url # but please rename it to slug
get the next url in the view (with get_next_by_FOO)
def news_view(request, url):
#[....]
try
next_url = news.get_next_by_published().get_absolute_url()
except Body.DoesNotExist
next_url = None
return #[....] and include next_url
use this next_url in your template
{% if next_url %}
next
{% endif %}