uploading files using python to a Django server - python

I have a simple view which receives files form users. I've tested it with my browser and it works okay.
On the other hand, I have a python and tested it against requestb.in and I can see the multipart/form-data and a http response code 200. So, both my view in receiving and storing files and the script in uploading multipart data work fine.
My problem being: When I use my script to upload a file to the view, even though I get http status code 200, I see no files being stored on the disk while in case of using the browser I do. What would be the problem you think? Here are my model, view and script:
models.py:
class FileUploads(models.Model):
uploads = models.FileField()
forms.py:
from django import forms
from .models import FileUploads
class UserProfileForm(forms.ModelForm):
class Meta:
model = FileUploads
fields = ['uploads']
views.py:
#csrf_exempt
def upper(request):
form = UserProfileForm(request.POST or None, request.FILES or None)
context = {'title': 'welcome', 'form': form}
if form.is_valid():
form.save()
context = {'form': form, 'title': 'thanks'}'
return render(request, 'upform.html', context)
Python script up.py:
import requests
f = open('C:\\Users\\Amir\\Desktop\\snow.jpg', 'rb')
urls='http://127.0.0.1:8000/upper'
r=requests.post(urls, files= {'a':f})
print(r.status_code)

It should be:
import requests
f = open('C:\\Users\\Amir\\Desktop\\snow.jpg', 'rb')
urls='http://127.0.0.1:8000/upper'
r=requests.post(urls, files= {'uploads':f})
print(r.status_code)
...the name of the file in the dict passed to requests.post needs to match the field name in the Django form :)

Related

Django: Use JSON and requests in my django app

I am using JSON, re and requests to get the number of followers from an instagram page:
import json
import re
import requests
PROFILE = 'espn'
response = requests.get('https://www.instagram.com/' + PROFILE)
json_match = re.search(r'window\._sharedData = (.*);</script>', response.text)
profile_json = json.loads(json_match.group(1))['entry_data']['ProfilePage'][0]['graphql']['user']
print(profile_json['edge_followed_by']['count'])
I am wondering how I can then add this to my django project to use that number to display on my website, updating each time someone vists the page to reflect the dynamic number. I had a look around but I dont really understand where you would add this code. I would image it would be added in the views section to then be rendered in the html page but not sure what the format would be.
The view for the page I would like it to be used for example is
View.py:
def index(request):
post = Post.objects.all()[0]
recent_posts = Post.objects.all()[1:4]
latest_posts = Post.objects.all()[:5]
data = {"post":post, "recent_posts":recent_posts, "latest_posts":latest_posts}
return render(request, 'index.html', data)
Also as an extra question - how would the process differ if I was using a Generic Class-Based view vs a function view.
Thank you very much.
Just get the data as you are - but instead of printing the number, put it in a variable and include it in your template context:
def index(request):
post = Post.objects.all()[0]
recent_posts = Post.objects.all()[1:4]
latest_posts = Post.objects.all()[:5]
PROFILE = 'espn'
response = requests.get('https://www.instagram.com/' + PROFILE)
json_match = re.search(r'window\._sharedData = (.*);</script>', response.text)
profile_json = json.loads(json_match.group(1))['entry_data']['ProfilePage'][0]['graphql']['user']
page_visitors = profile_json['edge_followed_by']['count']
data = {"post":post, "recent_posts":recent_posts, "latest_posts":latest posts, "page_visitors":page_visitors}
return render(request, 'index.html', data)
In a class based view you would add this code to the get_context_data method.
Note though that this approach really isn't great - you appear to be scraping content from the Instagram webpage, which means you are heavily reliant on how that works, and your code could unexpectedly break at basically any time. I do not know if Instagram has an API that allows access to this information - but they likely do, and I would advise to make your requests to that instead.
You can give the JSON profile_json as context of your function view and just access each element of the JSON in the Django Template.
import json
import re
import requests
def index(request):
PROFILE = 'espn'
response = requests.get('https://www.instagram.com/' + PROFILE)
json_match = re.search(r'window\._sharedData = (.*);</script>', response.text)
profile_json = json.loads(json_match.group(1))['entry_data']['ProfilePage'][0]['graphql']['user']
context = {'profile': profile_json}
return render(request, 'index.html', context)
Now, assuming that profile_json has a structure of {'followers': 2000}, you can access it in the Django template as follows: {{ profile.followers }} since the JSON was given under the name profile to the context of the view.
If you want to use Generic Class-Based Views, I suggest to use a TemplateView.
from django.views.generic import TemplateView
class ProfileTemplateView(TemplateView):
template_name = 'index.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
PROFILE = 'espn'
response = requests.get('https://www.instagram.com/' + PROFILE)
json_match = re.search(r'window\._sharedData = (.*);</script>', response.text)
profile_json = json.loads(json_match.group(1))['entry_data']['ProfilePage'][0]['graphql']['user']
context['profile'] = profile_json
return context
Next, you can access the profile context the same way as before in your Django template.

Calling a Django-Rest API from a Django Form

I built a Django-Rest API with an APIView that uploads a file to a folder of a web server.
This API is working with Postman as shown in the pictures below:
Now, I am working on calling this API from the below HTML form:
Issue I am facing: the file sent via the form returns the following error:
"file": [
"No file was submitted."
]
Probably something related with the binding of the file as the file is uploaded in the form but not sent to the API.
Below the code of my application:
index.html
<form action="/file/upload/" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<input id="audio_file" type="file"/>
<input type="submit" value="Upload File" name="submit"/>
</form>
views.py
class IndexView(TemplateView):
template_name = "index.html"
log = logging.getLogger(__name__)
log.debug("Debug testing")
def post(self, request): # TODO: fix, empty file error returned after calling post method
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
# https://docs.djangoproject.com/en/2.2/ref/forms/api/#binding-uploaded-files
form = FileForm(request.POST, request.FILES)
# check whether it's valid:
if form.is_valid():
instance = form.save(commit=False)
instance.save()
# redirect to the same URL:
return HttpResponseRedirect('/App/index/')
# if a GET (or any other method) we'll create a blank form
else:
form = FileForm()
return render(request, 'index.html', {'form': form})
class FileView(views.APIView):
parser_classes = (MultiPartParser, FormParser)
def post(self, request):
'''This method is used to Make POST requests to save a file in the media folder'''
file_serializer = FileSerializer(data=request.data)
if file_serializer.is_valid():
file_serializer.save()
return Response(file_serializer.data, status=status.HTTP_201_CREATED)
else:
return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST)
models.py
from django.db import models
class FileModel(models.Model):
file = models.FileField()
timestamp = models.DateTimeField(auto_now_add=True)
forms.py
from django.forms import ModelForm
from App.models import FileModel
class FileForm(ModelForm):
# Creating a form that maps to the model: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/
class Meta:
model = FileModel
fields = ['file']
Below the documentation I have already consulted without success:
https://docs.djangoproject.com/en/2.2/topics/http/file-uploads/
https://docs.djangoproject.com/en/2.2/topics/forms/
These are the stackoverflow questions I already read without finding a solution to the issue:
Django Contact Form Attachment showing 'This field is required.' What am I doing Wrong?
Django calling REST API from models or views?
Django HTML Form Send Attachment Emails
Post to django rest framework
Complete code repository: https://github.com/marcogdepinto/Django-Emotion-Classification-Ravdess-API .
EDIT: I changed the if statement inside IndexView.post as follows
if form.is_valid():
instance = form.save(commit=False)
instance.save()
Now the request is OK but the file passed is empty
HTTP 201 Created
Allow: POST, DELETE, OPTIONS
Content-Type: application/json
Vary: Accept
{
"file": null,
"timestamp": "2019-08-16T06:15:58.882905Z"
}

How to return data from an API in Django?

Im trying to learn how to use APIs in Django and I want to return some simple data from one within a web page in html. The API is Mozscape and when running it in a terminal one can obtain the score of a website out of 100 like so:
from mozscape import Mozscape
client = Mozscape(
'api_user_id',
'secret_key')
url = 'http://www.google.com'
get_da = client.urlMetrics(url, cols=68719476736)
print(get_da)
and this prints the following
{u'pda': 100}
the '100' is all I want there. I want a user to enter a url into a form in a page in Django and to get that score int back so I have made the following models, views and form
class DomainAuthority(models.Model):
url = models.URLField(max_length=300)
def __str__(self):
return self.url
class Meta:
verbose_name = 'Domain'
verbose_name_plural = 'Domains'
views.py
def DomainAuthorityView(request):
form = DomainAuthorityForm(request.POST or None)
if form.is_valid():
new_domain = form.save(commit=False)
new_domain.save()
return render(request, 'domain_authority.html', {'form': form})
forms.py
class DomainAuthorityForm(forms.ModelForm):
class Meta:
model = DomainAuthority
fields = ['url']
so I have the form working and when a url is entered in the html form its saved in the admin backend but what I dont know how to do now is how to pass that url into the Mozscape API so that I can get the score back.
I took a look at the Django rest framework and installed it and followed some quick tutorial videos on Youtube and other places but in those examples they were taking saved Django objects such as blog posts and returning them as JSON data which is not what I want to do.
I tried import the API into the views file and then adding this line into then view
get_da = client.urlMetrics(new_domain, cols=68719476736)
but then I get this error after entering the url into the form in the web page
<DomainAuthority: https://www.google.com> is not JSON serializable
what do I need to do here to pass the user inputted urls to the API and return the correct response in a web page?
thanks
EDIT - UPDATED VIEW as of 19th Aug
def DomainAuthorityView(request):
form = DomainAuthorityForm(request.POST or None)
if form.is_valid():
new_domain = form.save(commit=False)
new_domain.save()
response = requests.get(new_domain.url, cols=68719476736)
#response = requests.get(client.urlMetrics(new_domain.url, cols=68719476736))
json_response = response.json()
score = json_response['pda']
return render(request, 'domain_authority_checked.html', {'score': score})
else:
return render(request, 'domain_authority.html', {'form': form})
so now it should redirect after successful form completion with url and the url is passed to the API to get the score and the redirects to 'domain_authority_checked.html' with just this
{{ score }}
so I have two outcomes here, if I pass in 'client.urlMetrics' into response I can load the 'domain_authority.html' but after a url his input into the form an error page returns with this
InvalidSchema at /domainauthority/
No connection adapters were found for '{'pda': 100}'
if I dont pass 'client.urlMetrics' to response then Django doesn't know what 'cols' is and returns this
TypeError at /domainauthority/
request() got an unexpected keyword argument 'cols'
I suggest this approach:
import requests
response = requests.get(url)
json_response = response.json()
score = json_response['key_name']
You can then simply render a template, add the score to the template context and display the value using {{ }}.
You may also want to define a rest_framework serializer (otherwise you don't need django_rest_framework) and verify the response against this serializer in order to ensure that you've received what you expected:
serializer = MySerializer(data=json_response)
if serializer.is_valid():
score = json_response['key_name']
You can use:
return HttpResponse(json.dumps(data), content_type='application/json')
instead of render the form. Only you need to import json in the header and create an empty dict named "data".

how to clear form data in django

I have created django form.py file :
from django import forms
from models import ResumeModel
class ResumeForm(forms.ModelForm):
username = forms.CharField(max_length = 200)
first_name = forms.CharField(max_length = 200)
last_name = forms.CharField(max_length = 200)
fathers_name = forms.CharField(max_length = 200)
email = forms.EmailField(required=True, label='Your e-mail address')
message = forms.CharField(widget=forms.Textarea)
class Meta():
model = ResumeModel
fields =('username','first_name','last_name','fathers_name','email','message')
views.py :
def save(request):
if 'submit' in request.POST:
form = ResumeForm(request.POST)
if form.is_valid():
form.save()
form = ResumeForm()
return render(request, '/success/', {'form': form})
else:
form = ResumeForm()
args = {}
args.update(csrf(request))
args['form'] =form
return render_to_response('create.html',args)
urls.py:
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^save/', 'Resume.views.save'),
url(r'^success/$', TemplateView.as_view(template_name='success.html')),
)
Now all is working fine. problem is that as i submit form it moves to success page and when i go back to previous page that is /save/ page it holds all the values in fields that i submitted. How to clear the fields if i press back button to go back to that form page having url as "/save/ "
This is about the browsers behaviour. Browser shows u the page in its cache instead of calling a new page.
U may workaround this by using javascript.Unfortunately browser also dont run the javascript codes again.
But u may try :
$(window).bind("pageshow", function() {
// Clean form values
});
by JQuery
This is a browser implementation detail. You might be able to hack your way around it with JavaScript or break the cache etc. The subject is discussed in this stackoverflow question.
However a nicer UI approach might be to use AJAX to POST a serialised version of the form. If you successfully validate and process that form in your view (inserting a new row into your resume model table etc), you could then send a JSON response which might invoke some JavaScript to clear the form fields (maybe using .reset()). The user is then free to submit another form easily if that is the requirement.
Also note it is recommended that you use a HttpResponseRedirect after a successful POST (you have a render() response at the moment). This stops the users re-submitting the form again and potentially duplicating rows in your databases etc.

Upload file with Django Views

I am working on a feature for a Django application that uploads files (images in my context) to the server. Everything is working fine but I am wondering how I can receive as callback the uploaded file path.
Here is my View:
def post(self, request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['uploadedFile'])
return redirect('/')
else:
form = UploadFileForm()
return render_to_response('index.html', {'form': form})
My Form:
class UploadFileForm(forms.Form):
uploadedFile = forms.FileField()
And my handler:
def handle_uploaded_file(source):
fd, filepath = tempfile.mkstemp(prefix=source.name, dir=FILE_UPLOAD_DIR)
with open(filepath, 'wb') as dest:
shutil.copyfileobj(source, dest)
return filepath
I know that handle_uploaded_file(request.FILES['uploadedFile']) from my view is the required string that I need as callback but how to receive it in the response?
It is possible to receive instead of my index.html (it is there just for testing purposes) the path of the image for further manipulation in the frontend part.
I might sound like a noob but I really want if that works somehow.
Also is there a way for my View to handle multiple file upload? It is something that I need to change in my handler?
Sorry for putting so many questions...
Try this:
file_path = handle_uploaded_file(request.FILES['uploadedFile'])
return render_to_response('index.html', {'form': form, 'file_path': file_path})

Categories

Resources