Django - Send email with URL to newly uploaded file - django

I have a Django app where users can upload PDF files. The PDF files will be saved on my cloud provider. After successfully submitting the PDF, I want to send an email to the user with the URL to the PDF on my cloud. I've been trying to do it by overriding form_valid() but at that point, the URL is not yet generated. The URL also isn't hardcoded, so I can't just point to a hard coded URL in form_valid()
Any ideas on how to solve this?

Can you please provide us the code within form_valid() ? You need to insert your logic after super.form_valid() e.g.:
def form_valid(self, form):
ret = super().form_valid(form)
instance = form.instance
# get the filename and send an email
...
return ret

Related

google cloud storage images differ for authenticated url and public url

I cant seem to understand how is it possible that for GCS the authenticated URL shows a different image then the public URL ?
Im uploading the images via a python django script
def upload_to_cloud(blob_name, file_obj):
file_type = imghdr.what(file_obj)
blob_name = str(blob_name) + '.' + file_type # concatenate string to create 'file_name.format'
stats = storage.Blob(bucket=bucket, name=blob_name).exists(client) # check if logo with the same reg.nr exists
if stats is True: # if exists then delete before uploading new logo
storage.Blob(bucket=bucket, name=blob_name).delete()
blob = bucket.blob(blob_name)
blob.upload_from_file(file_obj=file_obj, content_type=f'image/{file_type}')
path = blob.public_url
return path
class CompanyProfile(SuccessMessageMixin, UpdateView): # TODO why company logo differs from the one in ads_list?
model = Company
form_class = CompanyProfileCreationForm
def form_valid(self, form):
"""
Check if user uploaded a new logo. If yes
then upload the new logo to google cloud
"""
if 'logo' in self.request.FILES:
blob_name = self.request.user.company.reg_nr # get company registration number
file_obj = self.request.FILES['logo'] # store uploaded file in variable
form.instance.logo_url = upload_to_cloud(blob_name, file_obj) # update company.logo_url with path to uploaded file
company = Company.objects.get(pk=self.request.user.company.pk)
company.save()
return super().form_valid(form)
else:
return super().form_valid(form)
Any ideas on what Im doing wrong and how its even possible? The file that I actually uploaded is the one under authenticated url. The file thats under public url is a file that I uploaded for a different blob
EDIT
Im adding screenshot of the different images because after some time the images appear to be the same as they should be. Some people are confused by this and comment that the images are the same after all
Public URL
Authenticated URL
Note that caching issue is ruled out since I sent the public URL to my friend and he also saw that the image is the HTML text although the image in the authenticated URL (the correct image) was a light bulb. He also noted that the URL preview in fb messenger showed the light bulb image but when he actually opened the URL the HTML text image appeared
This problem persists in case a file is uploaded with the same blob name. This happens regardless if its overwritten by gcs or if I previously execute blob delete function and then create a new file with the same name as the deleted blob.
In general the same object will be served by storage.googleapis.com and storage.cloud.google.com.
The only exception is if there is some caching (either in your browser, in a proxy, with Cloud CDN or in GCS). If you read the object via storage.cloud.google.com before uploading a new version, then reading after by storage.cloud.google.com may serve the old version while storage.googleapis.com returns the new one. Caching can also be location dependent.
If you can't allow an hour of caching, set Cache control to no-cache.

Get token from URL in Django. TDAmeritrade API Call

Ok,
I'm developing a website where I'm using Django. The website is for creating and keep track of stock portfolios. I have the database and the basics of the website set up but I would like to use the TDAmeritrade API in order to get the stock information. How it works is the user is redirected to TD where they enter there Login and Password they accept the terms and get transferred to a redirect page of the local host (until it goes live). Which looks a little like this
"https://127.0.0.1:8000/?code=" with a huge code after the equals sign.
Finally, how would one create the URL destination in Django url.py file and store the code for AUTH Token
I've tried something like this: path('?code=', test_view, name='test'),
but had no luck but that could be because of this error (You're accessing the development server over HTTPS, but it only supports HTTP.)
Thanks in advance!
Side note: I've tried looking up how Paypal does there send back confirmation but all I could find were packages Pre-build for Django
I figured out the solution with the help of Moha369 in the comments, So shout out to him/her!
def home_view(request):
context= {}
user = request.user
if user.is_authenticated:
token = request.GET.get('code')
print(token)
return render(request, "home.html", context)
Django Docs that helped

Django upload for processing

I'm looking for a way to upload file to django server. The thing is I'm not trying to save in I just to open it and get data for processing. I looked through some of the examples here, but I couldn't find anything that answers this. I'm probably just not looking for a correct thing please help.
I don't want to use models, just a simple website with a upload button.
Thanks!
Use a Form:
class UploadForm(forms.Form):
file = forms.FileField()
def process(self):
file = self.cleaned_data.get('file')
# do whatever you need here to process the file
# e.g. data = file.read()
In your view, call process() on your form after the user uploads the file and the form is successfully validated.
def my_view(request):
if request.method == 'POST':
form = UploadForm(files=request.FILES)
if form.is_valid():
form.process()
return ...
Depending on the size of the file and your Django settings for FILE_UPLOAD_HANDLERS, the file is discarded immediately after the view is done processing if MemoryFileUploadHandler is used. The operating system will also eventually discard the file if TemporaryFileUploadHandler is used.

Use Django views for handling blobstore uploads

In stead of using the BlobstoreUploadHandler supplied in AppEngine, I'd prefer to use a Django view, so I can keep all the urls and view functions together. However, I can't find out how to get the blob-key of the uploaded file! (like get_uploads() does for the upload handler). I saw that the BlobstoreUploadHandler uses request.params, but I don't think that is available from Django's Request.
def upload_form(request):
upload_url = blobstore.create_upload_url(reverse(upload_blob))
output = '<html><body>'
output += '<form action="%s" method="POST" enctype="multipart/form-data">' % upload_url)
output += ('''Upload File: <input type="file" name="file"><br> <input type="submit"
name="submit" value="Submit"> </form></body></html>''')
def upload_blob(request):
print request
# How to get the 'blob-key' from request?!
When I examine the request object, all I get is
<WSGIRequest
GET:<QueryDict: {}>,
POST:<QueryDict: {u'submit': [u'Submit']}>
# And COOKIES, META, etcetera
EDIT: Request.FILES
I discovered that some info can be extracted using Request.FILES, which gives:
<MultiValueDict: {u'file': [<InMemoryUploadedFile: my_file (message/external-body)>]}>
However, I assume that the blobstore still handles the file content (is that why it says "content_type=message/external-body"?), so I still need the key somehow. Calling read() gives:
Content-Type: application/octet-stream
MIME-Version: 1.0
Content-Length: 17
Content-MD5: ZmQ3OTJhNjMzNGE0OTAzNGU4NjE5MDNmMGEwNjliMGE=
content-type: application/octet-stream
content-disposition: form-data; name="file"; filename="a1_blob"
X-AppEngine-Upload-Creation: 2012-02-12 22:11:49.643751
So it looks like AppEngine actually replaced the file content by this descriptor, but still, where does AppEngine put the key?
I'm starting to suspect that the blob-key is just lost when not using the webapp framework, since the UploadedFile object has no key() method.
It took me a long time to find, but the content_type: message/external-body requires extra parameters, to find the actual file, in AppEngine's case, this is the blob-key. However, Django doesn't support these extra content_type parameters, so they are indeed lost in the process. There seems to be a patch, but I don't think it's in the AppEngine Django version yet.
https://code.djangoproject.com/ticket/13721
I had the same problem yesterday. Thanks to your post I realizad that the problem was django and his class views. I finally use a code that I have since 2011 and it still works. It does not use BlobstoreUploadHandler, but it gets the blob_infos from the request after automatically upload it to blobstore.
You can use that function in the next way from your callback django function or class (I finally did not try it in a class view from Django but I think it will work. Currently I'm using it in a function view from Django with its request):
media_blobs = get_uploads(request, populate_post=True)
The function is the next:
import cgi
from google.appengine.ext import blobstore
def get_uploads(request, field_name=None, populate_post=False):
"""Get uploads sent to this handler.
Args:
field_name: Only select uploads that were sent as a specific field.
populate_post: Add the non blob fields to request.POST
Returns:
A list of BlobInfo records corresponding to each upload.
Empty list if there are no blob-info records for field_name.
"""
if hasattr(request,'__uploads') == False:
request.META['wsgi.input'].seek(0)
ja = request.META['wsgi.input']
fields = cgi.FieldStorage(request.META['wsgi.input'], environ=request.META)
request.__uploads = {}
if populate_post:
request.POST = {}
for key in fields.keys():
field = fields[key]
if isinstance(field, cgi.FieldStorage) and 'blob-key' in field.type_options:
request.__uploads.setdefault(key, []).append(blobstore.parse_blob_info(field))
elif populate_post:
if isinstance(field, list):
request.POST[key] = [val.value for val in field]
else:
request.POST[key] = field.value
if field_name:
try:
return list(request.__uploads[field_name])
except KeyError:
return []
else:
results = []
for uploads in request.__uploads.itervalues():
results += uploads
return results
The last function is not mine. I do not remember where I got it three or four years ago. But I think it will help someone.
UPDATE:
You also can use a view handler of webapp.WSGIApplication and at the same time use django. This way will allow you to use BlobstoreUploadHandler and BlobstoreDownloadHandler (for video stream as example). You only need to add the view class in main.py and create its handler:
class ServeVideoHandler(blobstore_handlers.BlobstoreDownloadHandler):
def get(self, resource):
...
downloader_handler = webapp.WSGIApplication([('/pathA/pathB/([A-Za-z0-9\-\=_]+)', ServeVideoHandler),], debug=True)
And in app.yaml add the handler before the script main.application that contains your django app.
- url: /pathA/pathB/(.+)
script: main.downloader_handler
The key info isn't directly in the file, it's in file.blobstore_info.key()
post your form containing your image to a url created using blobstore.create_upload_url():
from google.appengine.ext import blobstore
upload_url = blobstore.create_upload_url('/add_image/')
the created url will save the image in the blobstore and redirect the request (with modified file object) to /add_image/
define a url pattern and view for /add_image/ and handle the image:
def add_action_image(request):
image = request.data.get('image')
image_key = image.blobstore_info.key()
... addl' logic to save a record with the image_key...
As you noted, BlobstoreUploadHandler is open source, so you can see the logic they use to parse the key out of the request params. Note that request.params just includes variables from both the query string and the request body (for POST requests). So you might want to start with your djnago request's REQUEST object.

Validation Error with Multiple File Uploads in Django via Ajax

I have a view to which I am trying to submit multiple ajax uploads via raw post data (e.g. via an octet-stream). These requests are submitted one after the other so that they process in parallel. The problem is that django thinks that only the last request is valid. For example, if I submit 5 files, the first four give:
Upload a valid image. The file you uploaded was either not an image or a corrupted image.
I'm guessing this occurs because somehow the requests overlap? And so the image isn't completely loaded before the form attempts to validate it?
And the last one works fine.
My upload view:
def upload(request):
form = UploadImageForm(request.POST, request.FILES)
print form
if form.is_valid():
# ..process image..
And my upload image form:
class UploadImageForm(forms.Form):
upload = forms.ImageField()
To submit the requests I'm using the html5uploader js pretty much right out of the box.
On a different not, have you tried https://github.com/blueimp/jQuery-File-Upload/ - is a pretty good non-flash based file uploader with progress bar.