Calling a Django-Rest API from a Django Form - python

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"
}

Related

Django redirect URL into another URL with form

I wouldlike to redirect in another URL when I submit my form.
After submit, I wouldike to have another page with something like "Hello (SummonerName) and Welcome" with URL like this : interface/main/SummonerName=(SummonerName)
Here my code :
forms.py
from django import forms
class SummonerForm(forms.Form):
summoner_name = form.CharField(label='SummonerName:', max_length=100)
views.py
from django.http import HttpresponseRedirect
from django.shortcuts import render
from .forms import NameForm
def get_name(request):
# 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:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/interface/name.html')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'interface/main.html', {'form': form})
and my main.html
<form action="/SummonerName={{ current_name }}" method="post">
<label for="summoner_name">SummonerName: </label>
<input id="summoner_name" type="text" name="summoner_name" value="{{ current_name }}">
<input type="submit" value="Rechercher">
</form>
EDIT :
urls.py
from django.urls import path
from . import views
urlpatterns = {
path('main', view.get_summoner_name)
}
Thank you !
That is what return HttpResponseRedirect does your views.py. You only need to pass your desired url as the first parameter instead of `'/interface/name.html'.
See:
https://docs.djangoproject.com/en/2.2/ref/request-response/#httpresponse-subclasses
For passing on the name, you have many options depending on what you want to do with the inputted data.
A simple solution:
Your example is rather simple and so if you only want to redirect to some url and show the name the user has inputed the easiest would be to pass the name (and any other parameters) via the url as a querystring:
import urllib.parse
# ... omitted code
def get_name(request):
# 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:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# get parameters from the form
params = {
"summoner_name" : form.validated_data["summoner_name"],
# multiple parameters can be passed in this dict e.g.
# "some_other param" : param_value
}
# encode the dictionary into a querystring
q_string = urllib.parse.urlencode(params)
# construct the new URL:
redirect_url = "/interface/success/?summoner_name={}".format(q_string)
return HttpResponseRedirect(redirect_url)
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'interface/main.html', {'form': form})
You can then access these parameters in your views/templates by the request object.
Then, in your urls.py you should match this url pattern by adding to your patterns something like:
path('interface/success/', views.success_view),
and you should of course define that view:
# views.py
def success_view(request):
return render(request, "success.html")
Finally you should add a template success.html. There you could do <h1> Hello {{ request.GET.summoner_name }}! </h1>
A more common usecase
The method above only shows data the user has inputted which is usually not very usefull... More commonly in a form we ask data that we need to save to the db, and thats why I proposed a ModelForm in my comment. It is off the scope of this answer to explain exactly how that would work but in a nutshell: you should define a model in your models.py (i.e. a db table) where you can store data (summoner_name would be a field in such a model).
class SimpleModel(models.Model):
summoner_name=models.CharField(max_length=256)
# other fields you may need
Then you can use a django's generic class based views e.g.
class SimpleFormView(CreateView):
model = SimpleModel
success_url = "interface/success"
urls.py will also need some editing and your tempaltes should comply with django naming conentions too. Do read this if you want to go down that way:
https://docs.djangoproject.com/en/2.2/topics/class-based-views/generic-editing/#form-handling-with-class-based-views

'SearchForm' object has no attribute 'get'

I'm trying to create SearchForm with DateField, but form don't see attribute 'get', when I send data method="post". Where is error?
forms.py
class SearchForm(forms.Form):
datee = forms.DateField(input_formats=['%Y-%m-%d'],
widget=forms.widgets.DateInput(format="%Y-%m-%d"))
views.py
def index(request):
search_form = search(request)
context = {'search_form': search_form}
return render(request, 'name/index.html', context)
def search(request):
if request.method == 'POST':
form = SearchForm(data=request.POST)
if form.is_valid():
#Do something for examlpe
HttpResponseRedirect(reverse("name:second"))
else:
form = SearchForm()
search_form = form
return search_form
index.html
<form method="post" action="{% url 'name:search' %}">
{% csrf_token %}
{{ search_form.as_p }}
<button name="submit">Search</button>
</form>
But I'm getting this log and don't understand where is error:
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8000/search_result
Django Version: 2.0.5
Python Version: 3.6.3
Traceback:
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\core\handlers\exception.py" in inner
35. response = get_response(request)
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\utils\deprecation.py" in __call__
97. response = self.process_response(request, response)
File "C:\Users\Александр\AppData\Local\Programs\Python\Python36\lib\site-packages\django\middleware\clickjacking.py" in process_response
26. if response.get('X-Frame-Options') is not None:
Exception Type: AttributeError at /search_result
Exception Value: 'SearchForm' object has no attribute 'get'
Django view should return httpresponse object. But your search view return form object instead. You can rewrite serch view to something like this to fix error:
def search(request):
if request.method == 'POST':
form = SearchForm(data=request.POST)
if form.is_valid():
#Do something for examlpe
HttpResponseRedirect(reverse("name:second"))
else:
form = SearchForm()
context = {}
context['search_form'] = form
return render(request, 'name/index.html', context)
Form isn't supposed to have get, post or other dispatched HTTP verb-matching methods as it's there to represent data and operate on it (more specifically, conveniently delegate any meaningful actions -- i.e. any other than validation and cleaning -- to the underlying infrastructure), not fulfill HTTP request -- the latter is the view's responsibility.
Your search view returns a SearchForm instance when it must return an HttpResponse-compatible object instead, and this is where the error comes from (note the if response.get('X-Frame-Options') is not None:).
To fix this, make sure to return render(request, 'name/index.html', {'form': search_form}) from the search view.

Django Page not found (404) Request Method: No Sales matches the given query [duplicate]

This question already has answers here:
Django: get() returned more than one items -- it returned 3
(4 answers)
Closed 5 years ago.
My python code keeps returning the following error:
Page not found (404) Request Method: GET Request URL:
http://127.0.0.1:8000/geo_gas/edit_sale/ Raised by:
geo_gas.views.edit_sale No Sales matches the given query.
class Sales(models.Model):
gas_qty = models.CharField(max_length=20)
amount = models.CharField(max_length=20)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.gas_qty
class Meta:
verbose_name_plural = 'Sales'
View.py
def edit_sale(request):
"""Edit an existing sales record."""
entry = get_object_or_404(Sales, pk=1)
if request.method != 'POST':
form = SalesForm(instance=entry)
else:
form = SalesForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('geo_gas:sales'))
context = {'entry': entry, 'form': form}
return render(request, 'ht/edit_sale.html', context)
Urls.py
.......
# Page for editing a sale entry
path('edit_sale/', views.edit_sale, name='edit_sale'),
.......
ht/templates/ht/edit_sale.html
Edit entry:
<form action="{% url 'geo_gas:edit_entry' %}" method='post'>
{% csrf_token %}
{{ form.as_p }}
<button name="submit">save changes</button>
</form>
I have not been able to identify what part of the code is causing the error to occur.
The error is saying that no Sales instance with pk=1 exists, as the error is likely thrown by get_object_or_404(Sales, pk=1). So you might want to check if that's really the case.
You can try checking the pk of your instance by doing Sales.objects.first().pk to see.
Update: How to make it dynamic
Before going into that, it might be useful to understand what RESTful API endpoints are like. But briefly in this context you might want to have something like this
# urls.py
path('sale/', views.list_sale, name='list_sale'),
path('sale/<int:pk>/', views.retrieve_update_delete_sale, name='retrieve_update_delete_sale') # names of the view can be whatever you want
What happens here is that the pk argument is passed from the URL (<int:pk>) to the view as an argument in the function-based view.
Accessing the arguments passed from URLs in your view
def retrieve_update_delete_sale(request, pk): # <-- you will be able to access it here
"""Edit an existing sales record."""
entry = get_object_or_404(Sales, pk=pk) # this is now dynamic!
if request.method != 'POST':
form = SalesForm(instance=entry)
else:
form = SalesForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('geo_gas:sales'))
context = {'entry': entry, 'form': form}
return render(request, 'ht/edit_sale.html', context)
So now if you want to access the Sale instance with pk=1, all you need to do is visit the url with /sale/1/, and so on.

FileField not processed on form POST

When trying to upload a file using a FileField, the form doesn't post the file data to the server.
It works perfectly if you use a text field, but for some reason it doesn't recognize the file, as it doesn't
show up on request.FILES, or request.POSTS.
MEDIA_ROOT and MEDIA_URL configuration:
MEDIA_ROOT = '/home/grove/pootleImages/'
MEDIA_URL = '/pootleImages/'
get_unit_context decorator in decorators.py:
def get_unit_context(permission_codes):
def wrap_f(f):
#wraps(f)
def decorated_f(request, uid, *args, **kwargs):
unit = get_object_or_404(
Unit.objects.select_related("store__translation_project",
"store__parent"),
id=uid,
)
_common_context(request, unit.store.translation_project,
permission_codes)
request.unit = unit
request.store = unit.store
request.directory = unit.store.parent
return f(request, unit, *args, **kwargs)
return decorated_f
return wrap_f
My forms.py method:
def unit_image_form_factory(language):
image_attrs = {
'lang': language.code,
'dir': language.direction,
'class': 'images expanding focusthis',
'rows': 2,
'tabindex': 15,
}
class UnitImageForm(forms.ModelForm):
class Meta:
fields = ('image',)
model = Unit
# It works if using a CharField!
#image = forms.CharField(required=True,
# label=_("Image"),
# widget=forms.Textarea(
# attrs=image_attrs))
image= forms.FileField(required=True, label=_('Image'),
widget=forms.FileInput(
attrs=image_attrs))
def __init__(self, *args, **kwargs):
self.request = kwargs.pop('request', None)
super(UnitImageForm, self).__init__(*args, **kwargs)
def save(self):
super(UnitImageForm, self).save()
return UnitImageForm
My models.py snippet:
class Unit(models.Model, base.TranslationUnit):
# ...
# ...
# It works if using a TextField!
#image = models.TextField(null=True, blank=True)
image = models.FileField(upload_to=".", blank=True, null=True)
# ...
# ...
My urls.py snippet:
url(r'^xhr/units/(?P<uid>[0-9]+)/image/?$',
'image',
name='pootle-xhr-units-image'),
My views.py method:
#require_POST
#ajax_required
#get_unit_context('translate')
def image(request, unit):
"""Stores a new image for the given ``unit``.
:return: If the form validates, the cleaned image is returned.
An error message is returned otherwise.
"""
# Update current unit instance's attributes
unit.uploaded_by = request.profile
unit.uploaded_on = timezone.now()
language = request.translation_project.language
form = unit_image_form_factory(language)(request.POST, request.FILES, instance=unit,
request=request)
if form.is_valid():
form.save()
context = {
'unit': unit,
'language': language,
}
t = loader.get_template('unit/image.html')
c = RequestContext(request, context)
json = {'image': t.render(c)}
rcode = 200
else:
json = {'msg': _("Image submission failed.")}
rcode = 400
response = simplejson.dumps(json)
return HttpResponse(response, status=rcode, mimetype="application/json")
My HTML template for the image upload:
<div id="upload-image">
<form enctype="multipart/form-data" method="post" action="{% url 'pootle-xhr-units-image' unit.id %}" id="image-form">
{% csrf_token %}
<input type="file" name="image" id="id_image" />
<p><input type="submit" value="{% trans 'Upload' %}" /></p>
</form>
</div>
When the form is instantiated, request.POST does not return the file browsed by the user, neither request.FILES.
form.errors just returns "This field is required"
The form object returns the following:
<tr><th><label for="id_image">Image:</label></th><td><ul class="errorlist"><li>This field is required.</li>
</ul><input lang="pl" rows="2" name="image" id="id_image" type="file" class="images expanding focusthis" dir="ltr" tabindex="15" /></td></tr>
And when the user clicks the submit button, the following POST error occurs:
"POST /xhr/units/74923/image HTTP/1.1" 400 35
I could bypass it by including required=False to the image property, but the file is not posted anyway.
More output debug information:
POST when fileField is required=True:
Status Code: 400 BAD REQUEST
Form Data:
csrfmiddlewaretoken: yoTqPAAjy74GH
form.errors:
"msg": "imageThis field is required."}
If change required=True to required=False:
Status Code: 200 OK
Form Data:
csrfmiddlewaretoken: yoTqPAAjy74GH
But the imagefield still doesn't show up in the form data.
Thank you,
Alex
I added a gist hub containing all files related to this problem, to ease visualization:
https://gist.github.com/alex-silva/40313734b9f1cd37f204
It looks like you've forgotten to add the {% csrf_token %} in your form. Add that between the tag.
OR...
You can add the csrf_exempt decorator to your processing view:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
More info: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
Finally managed to get it to work in some way. The form wasn't working at all when using it in the main page, so I created a link to a blank page containing just the upload image form. When separate from the main page, the upload works fine. Then I just redirect to the main page after uploading. Why the form doesn't work in the main page, that's a mystery.

How to get data from form using POST in django (Horizon Dashboard)?

i'm new in development using django, and i'm trying modify an Openstack Horizon Dashboard aplication (based on django aplication).
I implements one function and now, i'm trying to do a form, but i'm having some problems with the request.
In my code i'm using the method POST
Firstly, i'm want to show in the same view what is on the form, and i'm doing like this.
from django import http
from django.utils.translation import ugettext_lazy as _
from django.views.generic import TemplateView
from django import forms
class TesteForm(forms.Form):
name = forms.CharField()
class IndexView(TemplateView):
template_name = 'visualizations/validar/index.html'
def get_context_data(request):
if request.POST:
form = TesteForm(request.POST)
if form.is_valid():
instance = form.save()
else :
form = TesteForm()
return {'form':form}
class IndexView2(TemplateView):
template_name = 'visualizations/validar/index.html'
def get_context_data(request):
text = None
if request.POST:
form = TesteForm(request.POST)
if form.is_valid():
text = form.cleaned_data['name']
else:
form = TesteForm()
return {'text':text,'form':form}
My urls.py file is like this
from django.conf.urls.defaults import patterns, url
from .views import IndexView
from .views import IndexView2
urlpatterns = patterns('',
url(r'^$',IndexView.as_view(), name='index'),
url(r'teste/',IndexView2.as_view()),
)
and my template is like this
{% block main %}
<form action="teste/" method="POST">{% csrf_token %}{{ form.as_p }}
<input type="submit" name="OK"/>
</form>
<p>{{ texto }}</p>
{% endblock %}
I search about this on django's docs, but the django's examples aren't clear and the django's aplication just use methods, the Horizon Dashboard use class (how is in my code above)
When i execute this, an error message appears.
this message says:
AttributeError at /visualizations/validar/
'IndexView' object has no attribute 'POST'
Request Method: GET
Request URL: http://127.0.0.1:8000/visualizations/validar/
Django Version: 1.4.5
Exception Type: AttributeError
Exception Value:'IndexView' object has no attribute 'POST'
Exception Location:
/home/labsc/Documentos/horizon/openstack_dashboard/dashboards/visualizations/validar/views.py in get_context_data, line 14
Python Executable: /home/labsc/Documentos/horizon/.venv/bin/python
Python Version: 2.7.3
i search about this error, but not found nothing.
if someone can help me, i'm thankful
Your signature is wrong:
def get_context_data(request)
should be
def get_context_data(self, **kwargs):
request = self.request
Check the for get_context_data and the word on dynamic filtering
Since your first argument is the self object, which in this case is request, you are getting the error.
If you read more carefully the error message, it appears that the URL was retrieved using a GET method. Not POST:
AttributeError at /visualizations/validar/
'IndexView' object has no attribute 'POST'
Request Method: GET
Request URL: http://127.0.0.1:8000/visualizations/validar/
See the following link for an in deep explanation of GET vs POST
TemplateView by default will return a method not allowed 405 when you try to post to it. You can write your own post method for it:
class IndexView(TemplateView):
template_name = 'visualizations/validar/index.html'
def get_context_data(request):
#define your context and return
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def post(self, request, *args, **kwargs):
context = self.get_context_data()
if context["form"].is_valid:
print 'yes done'
#save your model
#redirect
return super(TemplateView, self).render_to_response(context)
If you're going to post from a form, use FormView instead, and you can still define context as you wish by overwriting get_context_data:
from django.views.generic import TemplateView, FormView
from forms import ContactUsEmailForm
class ContactView(FormView):
template_name = 'contact_us/contact_us.html'
form_class = ContactUsEmailForm
success_url = '.'
def get_context_data(self, **kwargs):
context = super(ContactView, self).get_context_data(**kwargs)
#context["testing_out"] = "this is a new context var"
return context
def form_valid(self, form):
# This method is called when valid form data has been POSTed.
# It should return an HttpResponse.
#form.send_email()
#print "form is valid"
return super(ContactView, self).form_valid(form)
contact_us = ContactView.as_view()
And urls.py:
from django.conf.urls import patterns, url
urlpatterns = patterns('contact_us.views',
url(r'^$', 'contact_us', name='contact_us'),
)
Hope this helps :) More on FormsView.

Categories

Resources