How to convert PDFTemplateResponse to bytes-like object in Django? - python

I am trying to use wkhtmltopdf to generate a PDF and send it as an attachment via email. Here is my view:
class MYPDFView(View):
template = 'pdftemplate.html'
def get(self, request):
data = {}
response = PDFTemplateResponse(
request=request,
template=self.template,
filename="hello.pdf",
context= data,
show_content_in_browser=True,
cmd_options={'margin-top': 10,
"zoom":1,
"viewport-size" :"1366 x 513",
'javascript-delay':1000,
'footer-center' :'[page]/[topage]',
"no-stop-slow-scripts":True},
)
email = EmailMessage(
'Hello',
'Body goes here',
'from#example.com',
['to1#example.com', 'to2#example.com'],
['bcc#example.com'],
reply_to=['another#example.com'],
headers={'Message-ID': 'foo'},
attachments=[('demo.pdf', response, 'application/pdf')]
)
email.send()
return response
The error I am getting is a TypeError and it says expected bytes-like object, not PDFTemplateResponse.
I am assuming that my response variable, which I am returning to see my PDF is not the type that I am supposed to provide in the attachments attribute.
My question is, how can I convert the PDFTemplateResponse to bytes-like object before providing it in attachments triple?

return_file = "tmp/hello.pdf"
temp_file = response.render_to_temporary_file("hello.html")
wkhtmltopdf(pages=[temp_file.name], output=return_file)
email.attach_file(return_file)
Source

Related

Serializer.is_valid() is always False

I have the following Serializer to handle a user:
class FriendSerializer(serializers.Serializer):
login = serializers.CharField(max_length=15, required=True)
password = serializers.CharField(max_length=15, required=True)
mail = serializers.CharField(max_length=50, required=True)
Currently, my view which processes the POST request to register a new user is, based on the Django REST tutorial:
#api_view(['POST'])
def register_new_user(request):
if request.method == 'POST':
print('POST request !')
stream = BytesIO(request.body)
data = JSONParser().parse(stream)
print(data)
serializer = FriendSerializer(data=data)
print(serializer.is_valid())
else:
print('Not a POST request!')
return HttpResponse('Nothing')
Thus, to simulate a client with a POST request, I use the following lines:
import requests
import json
json_data = json.dumps({'login': 'mylogin', 'password': 'mypassword', 'mail': 'mymail'})
r = requests.post('http://127.0.0.1:8000/register_new_user', json=json_data)
However, although the print(data) retrieves, as expected,
{"login": "mylogin", "mail": "mymail", "password": "mypassword"}
The serializer.is_valid() always returns False.
Am I missing any processing of my request?
EDIT:
I got the following info with serializer.errors:
{'non_field_errors': ['Invalid data. Expected a dictionary, but got str.']}
You dont have to convert a dictionary to string when using requests library's json option. This should work:
import requests
data = {'login': 'mylogin', 'password': 'mypassword', 'mail': 'mymail'}
r = requests.post('http://127.0.0.1:8000/register_new_user', json=data)
The solution is to use ast.literal_eval(request.data) to convert the string to a dictionary. I use request.data instead of manually parsing request.body.
However, v1k45's answer is the best solution, as I do not need to convert my dict to a string before sending my request.

Angular resource posts data but not receiving by django view

I have created an angular resource as
var services = angular.module('Services', ['ngResource']).
// SEND_REPLY_SMS
factory('SendSMS', ['$resource', function($resource){
return $resource('/bulk-sms/reply/', null,
{
send: {method: 'POST'},
}
);
}]);
I used it as
var data = $scope.data;
SendSMS.send({},data,
function(data){
console.log(data);
},function(error){
console.log(error);
}
);
I have checked with console.log(data), data contains the data and the browser shows that the post request has submitted the data.
But When I receive it in django view, I can not get the data in django view and my django view is
class ReplySMSView(View):
def post(self, request):
data = request.POST.copy()
print 'post data', request.POST # here data is not printed
data = dict(data.items())
return self.process(request, data)
def get(self, request):
data = request.GET.copy()
print 'get data', request.GET # here data is not printed
data = dict(data.items())
return self.process(request, data)
def process(self, request, data):
dct = {}
print data
model = IncomingMessage
account = request.user.account
contacts = data.get('contacts', '')
contacts = contacts if contacts else get_contacts_by_filter(model, data)
# TODO: get_contacts_by_filter is not working here for IncomingMessage
message = data.get('message', '')
identity = data.get('identity', '')
if not contacts:
dct['contacts'] = 'No contacts found.'
if not message:
dct['message'] = 'Message is required.'
if not identity:
dct['identity'] = 'Identity is required.'
if dct:
return HttpResponse(json.dumps(dct), content_type='application/json')
response = send_bulk_sms(contacts, message, identity, account, module='bulk')
return HttpResponse(response)
I am not getting where is problem in this code ?
AngularJS will post that data serialized into JSON, but django is expecting to receive form data. If you want to receive that data, you can change default behavior of AngularJS, fetch data not using POST, but rather request.body or you can use some third-party package, like Django REST framework to do job for you.
When calling ajax, you recieve encoded json string in request body, so you need to decode it using python's json module to get python dict.
As django is a web framweork, it expect data from a form.
I really reccomend using this framework http://www.django-rest-framework.org/
Anyway, to get post data will be like this in your view:
(Pdb) request.POST
<QueryDict: {}>
(Pdb) import json
(Pdb) json.loads(request.body)
{u'operator': u'pepe', u'password': u'1234', u'transport': u'LUUAAA'}
import json
class ReplySMSView(View):
def post(self, request):
data = json.loads(request.body)
print 'post data', request.POST # here data is not printed
data = dict(data.items())
return self.process(request, data)

Django 1.5 error when sending attachments 'file' object has no attribute '__getitem__'

I'm trying to send an attachment via email, a pdf but I get the following error 'file' object has no attribute '__getitem__'
Here's my code.
created_pdf = open(filename)
from_email = settings.DEFAULT_EMAIL
email = EmailMessage('KenyaBuzz Tickets for %s'%seat1.movie, "There's a pdf attached with the tickets, please print it and provide it at...", from_email,
[seat1.user.email],
)
email.attach(filename, created_pdf, 'application/pdf')
email.send()
The second parameter to attach should be the data, not the file object.
email.attach(filename, created_pdf.read(), 'application/pdf')
But it's better to use attach_file, which takes a filename directly:
email.attach_file(filename)

Django sending data to front-end. 'dict' object has no attribute '_meta'

I have these methods:
def get_all_from_database():
urls = Url.objects.all()
ips = Ip.objects.all()
context = {
'urls': serializers.serialize('json', urls),
'ip': serializers.serialize('json', ips)
}
return context
and the method that sends data to using ajax:
def send_results(request):
if request.is_ajax():
address = request.POST.get('url')
process_data(address, email_to, email_from)
context = get_all_from_database()
return HttpResponse(json.dumps(context), content_type='application/json')
But this raises error : INTERNAL SERVER ERROR 500 'dict' object has no attribute '_meta'.
Wheres the mistake, and how to correct it ?
You cant use serializers.serialize method with dict list that you got from values call:
urls = Url.objects.all().values('address', 'cnt')
Use default queryset:
urls = Url.objects.all()
ips = Ip.objects.all()
In you example context['urls'] value already in json format, and you cant use json.dumps() for json data.
You can use this example:
json.dumps({
'urls': Urls.objects.all().values_list('address', 'cnt'),
'ips': Ip.objects.all().values_list('address', 'cnt')
}), 'json')
urls = Url.objects.all().values('address', 'cnt')
ips = Ip.objects.all().values('address', 'cnt')
The above lines returns dict objects, try:
urls = Url.objects.all().values('address', 'cnt').values_list()
ips = Ip.objects.all().values('address', 'cnt').values_list()
Then you will have urls as a list containing the tuples:
[(address_1, cnt_1), (address_2, cnt_2), ...]
see: QuerySet API reference

Python error: __init__() takes at least 3 arguments (3 given)

so I'm working on a save profile feature and I'm not sure why I'm getting the following error:
__init__() takes at least 3 arguments (3 given)
This is the function it happens in (right after it hits the self.profiles.update line):
#view_config(route_name="profile", request_method='POST')
def save_profile(self):
try:
json = self.request.json_body
#username = str(json['userName'])
first_name = str(json['firstName'])
last_name = str(json['lastName'])
phones = str(json['phones'])
emails = str(json['emails'])
self.profiles.update(self, firstName=first_name, lastName=last_name, emails=emails, phones=phones)
value = {'result:': 'success', 'message': 'Profile Saved!'}
self.respond(value)
return self.route('profile')
except Exception, err:
print err
value = {'result:': 'error', 'message': 'There was an error processing the request'}
self.respond(value)
return self.route('profile')
The expanded console:
I did a project wide search for init and this seems like the only function that matches
class WhoView(Extension):
def __init__(self, context, request):
self.session = request.session
self.request = request
self.status_code = 200
self.content_type = "text/html"
ctx = self.session.ctx
self.request_context = context
Extension.__init__(self, ctx)
def attach_session(self, token):
self.ctx.attach_session(token)
Any thoughts? Additional info you need to see?
You're not redirecting anywhere (self.route does a redirect). Also, you can't redirect here because you're doing an ajax call. I'm pretty sure the assignment for phones and emails isn't correct because you're casting arrays to strings. We'll talk about it in the office tomorrow. Also as sza pointed out, you don't pass self for method calls.
#view_config(route_name="profile", request_method='POST')
def save_profile(self):
try:
json = self.request.json_body
#username = str(json['userName'])
first_name = str(json['firstName'])
last_name = str(json['lastName'])
phones = str(json['phones'])
emails = str(json['emails'])
self.profiles.update(firstName=first_name, lastName=last_name, emails=emails, phones=phones)
value = {'result:': 'success', 'message': 'Profile Saved!'}
except Exception, err:
print err
value = {'result:': 'error', 'message': 'There was an error processing the request'}
#returns a json response
return self.respond(value)
Isn't
self.profiles.update(self, firstName=first_name, lastName=last_name, emails=emails, phones=phones)
should be
self.profiles.update(firstName=first_name, lastName=last_name, emails=emails, phones=phones)

Resources