Change how GET url is displayed - django

Summary: If I search 'apple', the url will be http://127.0.0.1:8000/search_results?csrfmiddlewaretoken=rlUwb5Ju3Xr585FarH5eAGQJtpog83hqW4wRysbsMWM6eiO3prcKRONY28N118gR&query=apple&button= and I just want to know if there is a way to change this to something cleaner like 127.0.0.1:8000/search_results/apple?
Here is my code:
urls.py
path('search_results', views.search_results, name = 'search_results'),
html:
<form action="{% url 'search_results' %}" method="GET">
{% csrf_token %}
<input name = 'query' type = 'text'/>
<button type="submit" name="button">Search</button>
</form>
and views.py:
def search_results(request):
query =request.GET.get('query')
return HttpResponse(query)
I had tried to change these 3 lines in their respective files:
`path('search_results/<str:query>')`, # thought this would achieve /search_results/apple url
<form action="{% url 'search_results' 'query' %}" method="GET">
def search_results(request, query):
but this did not work. Does anyone know how I can achieve this?

The GET parameters are encoded in the query string [wiki]. The query string is not part of the path. There is thus no way to encode this that way. But you can indeed make a "redirect view" that moves it to the path.
You can change the redirect to:
# app/views.py
from django.shortcuts import redirect
def search_redir(request):
query =request.GET.get('query')
return redirect('search_results', query=query)
def search_results(request, query):
# …
pass
In your urls.py, you then define two views: one that will "catch" the initial GET request with the querystring, and one where you move the query to the path:
# app/urls.py
from django.urls import path
urlpatterns = [
path('search_results/', views.search_redir, name='search_redir'),
path('search_results/<str:query>/', views.search_results, name='search_results'),
]
In your form however, you still use the redirect view. Note that since you perform a GET request you do not need to use the {% csrf_token %}:
<form action="{% url 'search_redir' %}" method="GET">
<input name="query" type ="text"/>
<button type="submit" name="button">Search</button>
</form>

Related

Uploading and displaying an image in Django

I am currently learning Django. I am building a system that gets input from the user as image and throws back the damage level. I have the python files all caught up. But I am having difficulty doing the Django part.
How do I get an image as input from the user ?
And how will I throw back the result of the code that does the damage level prediction to the front end ?
Thanks in advance
To get images from user you can do as following. However getting data from request is not recommended way.
Views.py
from django.shortcuts import render, render_to_response
from django.core.files.storage import FileSystemStorage
def index(request):
if request.POST:
if request.method == 'POST' and request.FILES['image']:
uploaded_image = request.FILES['image']
fs = FileSystemStorage()
filename = fs.save(uploaded_image.name, uploaded_image)
uploaded_file_url = fs.url(filename)
code = get_code(uploaded_file_url)
return render_to_response("homepage.html", {"code": code})
else:
return render(request, "homepage.html")
Using this request.FILES['image'] we are getting a image from our form passed on post event.
In your template homepage.html
<form method="post" action="{% url 'code' %}" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="image" id="file-input" accept="image/x-png,image/jpeg,image/jpg"/>
<input type="submit" style="background: #0b0b0b" class="btn btn-secondary" value="Generate Caption"/>
<br><br>
<div style="background: #0b0b0b">
<a>Code for image is : {{ code }}</a>
</div>
</form>
{% url 'code' %} this will reverse the url by name as defined in urls.py. {% csrf_token %} is required for security reasons. enctype="multipart/form-data" is required to pass data like images, files etc. Input with name name="image" is what we are looking in request.FILES['image']. If you want change the name make your to change it everywhere.
And in your urls.py
from django.conf.urls import url
urlpatterns = [
url(r'^$', views.homepage, name='code'),
]
Also you can have look at Django forms for a better way.

Getting csrfmiddleware token in url

I am getting csrf token in the url after submitting my form like this.
http://127.0.0.1:8000/detail/?csrfmiddlewaretoken=lqvoeSG32IcodLTFksWEU1NPQ9XCmHybwmzMKEuPzxDN1e73B0JORpAGOcGGxsjH&symbol=FLWS
After making a GET request to view, the url is showing the csrf token in the url.
/views.py
def search(request):
if(request.method=='GET'):
form=searchform(request.GET)
if(form.is_valid()):
id=request.GET['symbol']
data=company.objects.filter(Symbol=id)
form=searchform()
return render(request, 'list-company.html',{"data":data,"form":form})
/urls.py
from django.contrib import admin
from django.urls import path
from csv2db.views import Company,search
urlpatterns = [
path('admin/', admin.site.urls),
path('company/',Company,name='company-details'),
path('detail/',search,name='search')
]
form in HTML file
{% block content %}
<form method="get" action="{% url 'search' %}">
{% csrf_token %}
{{ form.as_ul}}
<button type="Submit">Submit</button>
</form>
You are adding csrf_token template tag in the HTML file and form method is set to get. So data is appended as query parameters including csrf token.
So you can either change it to post method or remove the csrf_token template tag.
{% block content %}
<form method="POST" action="{% url 'search' %}">
{% csrf_token %}
{{ form.as_ul}}
<button type="Submit">Submit</button>
</form>
and change your view
def search(request):
form=searchform()
if(request.method=='POST'):
form=searchform(request.POST)
if(form.is_valid()):
id=request.GET['symbol']
data=company.objects.filter(Symbol=id)
form=searchform()
return render(request, 'list-company.html',{"data":data,"form":form})
return render(request, 'list-company.html',{"form":form})

Django form POST with NoReverseMatch error

In html:
<form role="form" method="post" action="{% url 'myapp:add_review' %}" enctype="multipart/form-data">
{% csrf_token %}
<div class="form-group">
<div class="col-sm-10">
<input id="review" name="review" type="text">
</div>
</div>
<button type="submit" class="btn btn-default"> Submit </button>
</form>
In views:
def add_reviews(request):
if request.method == "POST":
print "Post is here:", request.POST['review']
return render(request, 'myapp/single_item.html')
//or this?? return redirect('myapp:single_item')
In urls.py
app_name = 'myapp'
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
# register, login, logout
url(r'^register/$', views.UserFormView.as_view(), name='register'),
url(r'^login/$', views.login_user, name='login'),
url(r'^logout/$', views.logout_user, name='logout'),
# individual article page
url(r'^(?P<item_id>[0-9]+)/$', views.single_item, name='single_item'),
url(r'^(?P<item_id>[0-9]+)/add_review/$', views.add_review, name='add_review'),
]
Really basic stuff, in each individual item page, I want a form to write and post reviews for each item, and go back to the same item page.
When I'm in the individual item page, gives me the following error:
Reverse for 'add_review' with arguments '('',)' and keyword arguments '{}' not found.
1 pattern(s) tried: ['(?P<item_id>[0-9]+)/add_review/$']
Generally, these types of errors indicate that something is not right with an URL, that such pattern was not found, that it misses some parameters and so forth. From this particular error it seems that you are not passing item_id to the url matcher. Hence, Django cannot find an appropriate URL. You have:
url(r'^(?P<item_id>[0-9]+)/add_review/$', views.add_review, name='add_review')
Notice that you wrote ?P<item_id>[0-9]+)/..., which means you must provide item_id whenever you want this URL to be resolved. Try adding item_id as a keyword argument:
<form role="form" method="post" action="{% url 'myapp:add_review' item_id= ... %}" enctype="multipart/form-data">
This should construct a valid url (e.g., .../1/add_review/), which should be recognised by Django. However, I believe your add_review() function will also need to take an item_id argument, otherwise you might get TypeError due to unexpected function arguments.
As for the return value, I would use redirect instead of render, as you are, well, redirecting after a successful POST. Though you might need to pass a item_id, too, since your URL config specifies that: a) URL should have item_id and b) single_item() expects item_id. Something like this should work:
def add_review(request, item_id):
...
return redirect('myapp:single_item', {'item_id': item_id})
And signature of single_item should be something like this: single_item(request, item_id).

Proper way to handle multiple Django forms in one page with two views?

I've struggled with this problem for the last two days and could use some help. The home page for my Django 1.6 application will include two forms, one that a user can use to sign in to the site and one they can use to sign up (create a login) for the site:
# templates/home/home_page.html
<div class="sign-in-form">
<form action="{% url 'apps.home.views.sign_in' %}" method="post">
{% csrf_token %}
{{ sign_in_form.as_p }}
{% if next %}
<input type="hidden" name="next" value="{{ next }}">
{% else %}
<input type="hidden" name="next" value="{% url 'view-members' %}">
{% endif %}
<input type="submit" value="Sign in">
</form>
</div>
<div class="sign-up-form">
<fieldset>
<legend>Sign up</legend>
<form action="{% url 'apps.home.views.sign_up' %}" method="post">
{% csrf_token %}
{{ sign_up_form.as_p}}
<p><input type="submit" value="Sign up" /></p>
</form>
</fieldset>
</div>
If the user submits, the sign_in form, they'll be taken to a page where they can view other site members. If they submit the sign_up form, they'll be taken to a second signup page where they'll create a user profile, etc.
Originally, I was going to use the technique shown in this question and use one view to handle the homepage. However, I decided to try to use two views because I'm using the Django's actual login view (django.contrib.auth.views.login) so that I can add code to it to detect the user's device (phone, tablet, or computer), and merging that view with my sign_up view would create a very long and complicated view to maintain. I'd prefer to keep the views for both forms separate.
Here's the home page and sign_in views:
# apps/home/views:
def home_page(request, template):
sign_in_form = SignInAuthenticationForm()
sign_up_form = CreateAccountForm()
return render(request, template, {"sign_in_form": sign_in_form,
"sign_up_form": sign_up_form})
#sensitive_post_parameters()
#csrf_protect
#never_cache
def sign_in(request,
template='home_page.html',
redirect_field_name=REDIRECT_FIELD_NAME,
# authentication_form=AuthenticationForm,
authentication_form=SignInAuthenticationForm,
current_app=None, extra_context=None):
# Do device detection here...
# django.contrib.auth.views code goes here...
return response
The signup view will just be your typical, function-based view for processing a form as described in the Django documentation.
What I'm struggling with is my URLconf files. Here's my main and "home" URLconf files:
# conf/urls.py
urlpatterns = patterns('',
url(r'^$', include('apps.home.urls')),
# Other url patterns...
)
# apps/home/urls.py
urlpatterns = patterns('apps.home.views',
url(r'^$',
'home_page',
{'template': 'home/home_page.html'},
name='home-page'),
url(r'^sign_in/$',
'sign_in',
{'template': 'home/home_page.html'},
name='sign-in'),
url(r'^sign_up/$',
'sign_up',
{'template': 'home/home_page.html'},
name='sign-up'),
)
The problem is that I get this error during template rendering:
NoReverseMatch at /
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Request Method: GET
Request URL: http://localhost:8000/
Django Version: 1.6.2
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'apps.home.views.sign_in' with arguments '()' and keyword arguments '{}' not found. 1 pattern(s) tried: ['$sign_in/$']
Exception Location: /Users/smith/venv/swing/lib/python2.7/site-packages/django/core/urlresolvers.py in _reverse_with_prefix, line 429
Python Executable: /Users/smith/venv/swing/bin/python
Python Version: 2.7.5
Python Path:
['/Users/smith/Dropbox/www/swing',
'/Users/smith/venv/swing/lib/python2.7/site-packages/wurfl_cloud-1.0.1-py2.7.egg',
'/Users/smith/venv/swing/lib/python27.zip',
'/Users/smith/venv/swing/lib/python2.7',
'/Users/smith/venv/swing/lib/python2.7/plat-darwin',
'/Users/smith/venv/swing/lib/python2.7/plat-mac',
'/Users/smith/venv/swing/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/Extras/lib/python',
'/Users/smith/venv/swing/lib/python2.7/lib-tk',
'/Users/smith/venv/swing/lib/python2.7/lib-old',
'/Users/smith/venv/swing/lib/python2.7/lib-dynload',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-darwin',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac',
'/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/plat-mac/lib-scriptpackages',
'/Users/smith/venv/swing/lib/python2.7/site-packages']
At first I started to think that maybe it's telling me that it can's find the correct URL pattern in my home/urls.py file because the URL signature in my form is incorrect. Maybe I needed to do this to match the arguments in the sign_in view:
<form action="{% url 'apps.home.views.sign_in' 'home/home_page.html' %}" method="post">
But I'm already showing the template name in the home URLconf. And I don't think I need to pass the other view arguments in the form action (e.g. redirect_field_name) because their optional. In any case, adding this argument to the form action didn't fix it.
One of the things that confuses me is how to set the first url argument. I've set them to r'^sign_in/$' and r'^sign_up/$' because if I set them both to r'^$', the page will render properly but when I submit either form, it justs posts back to the home page. You can see this will happen by doing a "view source" on the page. It shows each form's action will be "/". On the other hand, the way I have it now seems incorrect to me because the site won't actually have a "/sign_in/" and "/sign_up/" URL since both forms are on the home page. Also, is there going to be a problem in which if the user submits one for or the other improperly, errors for both forms will be rendered on the page?
The Django documentation, to the best of my knowledge, doesn't really describe a standard approach for doing what I'm trying to do. It describes how to render multiple versions of the same form. Can anyone tell me what I'm doing wrong?
Thanks.
Your form names are 'sign_in_form' and 'sign_up_form', but in your html you wrote them 'form.as_p' instead of 'sign_in_form.as_p' and 'sign_up_form.as_p' this is the first bug a saw in your code.
The real problem is in your urls configuration. In your main urls.py you have
url(r'^$', include('apps.home.urls')),
Other ...
Though you will not be able to get to localhost:8000/sign_in/ because initially it does not satisfy to ^$ .
Try to change it by
url(r'', include('apps.home.urls')),
and put it to the end of urls.py.
i test this see if this what you want:
view.py
def loginUser(request,**Kargs):
LoginFormSet = formset_factory(LoginForm)
SignFormSet = formset_factory(SignForm)
if request.method == 'POST':
login_formset = LoginFormSet(request.POST, prefix='login')
sign_formset = SignFormSet(request.POST ,prefix='sign')
if login_formset.is_valid():
#do somthing
elif sign_formset.is_valid():
#do somthing
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
else:
login_formset = LoginFormSet(prefix='login')
sign_formset = SignFormSet(prefix='sign')
return render(request, 'reservetion/login.html',{'login_formset': login_formset,'sign_formset':sign_formset})
page.html:
<form action="{% url 'loginUser' %}" method="post">
{% csrf_token %}
{{ login_formset.management_form }}
{% for form in login_formset %}
{{ form }}
{% endfor %}
{{ sign_formset.management_form }}
{% for form in sign_formset %}
{{ form }}
{% endfor %}

Django NoReverseMatch url issue

I'm getting the error
"Reverse for 'recall' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'associate/recall/']"
When I try to submit a form. Here is my html:
<form action="{% url 'associate:recall' ordered_group %}" method="post">
{% csrf_token %}
<div>
<label for="recall">enter as many members of {{ ordered_group }} as you can recall </label>
<input type="text" id="recall" name="recall">
</div>
<div id="enter_button">
<input type="submit" value="enter" name="enter" />
</div>
<div id="done_button">
<input type="submit" value="done" name="done" />
</div>
</form>
"ordered_group" is a model object that is carried over from the 'learn' view:
urls.py:
urlpatterns = patterns('',
url(r'^learn/', "associate.views.learn", name='learn'),
url(r'^recall/', 'associate.views.recall', name='recall'),
url(r'^$', "associate.views.index", name='index'),
)
I am trying to use the ordered_group model object that is submitted in the learn view context to the html, back to the recall view as an argument. Can one do this? It makes sense to me, but what is the correct way of doing this?
views.py
def recall(request, ordered_group):
...
def learn(request):
...
ordered_group = ordered_groups[index]
return render(request, 'associate/learn.html', {'dataset':model, 'ordered_group':ordered_group})
I want to submit the form with
In you HTML, you are doing:
{% url 'associate:recall' ordered_group %}
Django expects that "recall" url is in "associate" namespace, because of the ":". But, you need to declare the namespace in urls.py, like:
url(r'^recall/', 'associate.views.recall', namespace='associate', name='recall')
If you don't want the namespace, just do:
{% url 'recall' ordered_group %}
And, about "ordered_group", you need to declare it in your url, like:
url(r'^recall/(?P<ordered_group>\w+)', 'associate.views.recall', namespace='associate', name='recall')
You are passing ordered_group in HTML, youare expecting this in views.py, but you are not expecting this on you URL.