'SearchForm' object has no attribute 'get' - python

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.

Related

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

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.

Using Django Context Processors with forms

I have multiple forms to be shown everywhere in my project and hence I read that having a context_processor was the best way to do it. So, I created one inside my app and it looks something like this:
def forms_processor(request):
name_form = NewNameForm()
work_form = NewWorkForm()
address_form = NewAddressForm()
context = {'name_form': name_form,
'work_form': work_form,
'address_form': work_form,
}
return context
This works great, I can just use {{name_form}} anywhere in my templates and that renders the form.
Now my question is, where do I validate the form? In my views.py or the context_processors.py? Right now my views for name_form looks something like this:
def user_profile(request):
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
else:
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
return render (request, 'user_profile.html', ctx)
This isn't working actually, if I submit an invalid form, it just comes back to the same page and won't show a populated form.
If someone could guide me or redirect me to some docs on this topic, that'd be awesome! Thanks!
The problem is that your processor instantiates the form on each render. Each time you call render, your processor is called, which instantiates a new form and displays THAT form, not the form instance that you created in the view. Therefore, the form being rendered is a blank instance but the form that contains the input and errors was destroyed by garbage collection after finishing your view.
A way I would do this, is passing the form you create in the view back to context before rendering. Pass it in to a context key such as "name_form_filled". Then if that variable is present in the context, don't render "name_form", instead render "name_form_filled".
views.py
def user_profile(request):
ctx = {}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save() # you named this named_form, not form.
# If you want to redirect to another view when the form is saved successfuly, do it here.
else:
ctx["name_form_filled"] = form
else:
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
{% if name_form_filled %}
<!-- Render form that has input and errors from previous POST. -->
{{ name_form_filled }}
{% else %}
<!-- render empty initial form. User has not attempted to submit yet. -->
{{ name_form }}
{% endif %}
</div>
===========================================================================
Another way you could do this is turn this view into a class based view and inherit a base class based view. This base class will override the get_context_data method and add your three forms. Note that you won't be using the context processor with this methodology so you could get rid of it if wanted in this case.
All views that use your form will extend the base view class. Then, after evaluating your form, if it is invalid, overwrite your name_form context key with the invalid form instance, which will be in your context.
views.py
class BaseView(View):
def get_context_data(self, *args, **kwargs):
context = {
"name_form": NewNameForm(),
"work_form": NewWorkForm(),
"address_form": NewAddressForm()
}
return context
class UserProfileView(BaseView):
def get(self, request, *args, **kwargs):
# Do GET logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
ctx.update({'title': 'Profile', 'active_tab': 'Profile'})
return render (request, 'user_profile.html', ctx)
def post(self, request, *args, **kwargs):
# Do POST logic here.
ctx = self.get_context_data(*args, **kwargs) # BaseView.get_context_data will be called here unless you override it in this class.
name_form = NewNameForm(request.POST)
if name_form.is_valid():
name_form.save()
else:
ctx["name_form"] = name_form # will replace the empty form in context with the form instance created in name_form that has input and errors.
return render (request, 'user_profile.html', ctx)
user_profile.html
<div id="form_container">
<!-- Will render whatever is in name_form. If this is after the
user has submitted an invalid form, this form will be populated with input and errors because we overwrote it in the view. -->
{{ name_form }}
</div>
===========================================================================
I personally think that the first solution is the best but when you start getting more complex, you should probably switch over to the second solution as class based views make complex views way easier.
Direct answer: you validate the form in views.py with is_valid() method. What you need is to populate context with bound form if the form is invalid:
def user_profile(request):
ctx = {'title': 'Profile', 'active_tab': 'Profile'}
if request.method == 'POST':
name_form = NewNameForm(request.POST)
if name_form.is_valid():
form.save()
return redirect(YOUR_REDIRECT_URL) # Always redirect after successful POST
ctx['form'] = form # if form is invalid return it with context
return render (request, 'user_profile.html', ctx)
Read more in documentation.

Django registration | change behaviour

After succesfully registering, the user is redirected to the template 'registration_done.html'.
Is there any way of changing this behaviour to redirecting the user to the registration page and displaying a message?
I tried these code below also tried different ways to change it but have different types of errors in different cases.
urls.py
url(r'^register/$',
views.register,
{
'success_url': '/accounts/register/?success=true'
},
name='register'),
view.py
def register(request):
if request.method == 'POST':
user_form = UserRegistrationForm(request.POST)
if user_form.is_valid():
# Create a new user object but avoid saving it yet
new_user = user_form.save(commit=False)
# Set the chosen password
new_user.set_password(user_form.cleaned_data['password'])
# Save the User object
new_user.save()
success = request.GET.get('success', None)
return render(request, {'new_user': new_user, 'success': success})
else:
user_form = UserRegistrationForm()
return render(request, 'account/register.html', {'user_form': user_form})
registration.html:
{% if success %}
<p>{% trans 'Successfull registration!' %}</p>
{% endif %}
Whats wrong I did?!
Error:
Traceback (most recent call last):
File "C:\Python34\lib\site-packages\django\core\handlers\base.py", line 149, in get_response
response = self.process_exception_by_middleware(e, request)
File "C:\Python34\lib\site-packages\django\core\handlers\base.py", line 147, in get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
TypeError: register() got an unexpected keyword argument 'success_url'
[18/Aug/2016 14:17:55] "GET /en/account/register/ HTTP/1.1" 500 59886
You are trying to pass the kwarg success_url to your function register
Your function register(request) only accepts one argument: 'request'.
So you can accept a second argument, say success_url, like so
def register(request, success_url):
...
But there is no point doing that if success_url is a constant. In that case, just define it your register function
def register(request):
success_url = 'foo'
Another point here is that you want to reverse that url, rather than hard code it.
Also I'm not sure why you would want to use this:
success = request.GET.get('success', None)
Any user could submit their own success variable in the GET request. Are you expecting this from a form? Looks like a security vulnerability if the user can just say that the were successful in request.GET.
Right now you aren't actually rendering a template on success anyway because you are missing a template name/path from your first render call.
So either add a template or redirect them to another page.

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