django, views direct to a another html page - django

I am using Django for develop a website. The website is intended to use to search information stored in a MySQL database.
This is the current basic flow of the web site.
1) index.html - this has a form to select an option
2) according the option, users will redirect to search.html (include a form)
3) once the user provides the criteria, the result will be displayed in reply.html
In my views.py , I have two functions.
from django.shortcuts import render
from website.models import WebsiteRepository
from .forms import SearchForm
from .forms import SelectTypeForm
def Search(request):
if request.method == 'POST':
#do something
return render(request, 'reply.html', {'env_dict':env_dict})
else:
#do something
return render(request, 'search.html', context = context)
def index(request):
if request.method =='POST':
#do something
return render(request, 'search.html', context = context)
else:
#do something
return render(request, 'index.html', context= context)
When I go to index.html page, I can select a option and it will direct me to search.html. After, I fill the form there and submit, it wont give me the reply.html page.
I have a feeling that, I could make this work by changing urls.py.
from django.urls import path
from website import views
urlpatterns = [
path('', views.index, name='index'),
#path('search/', view.Search, name ='Search')
]
I tried to google it. But its too much details and Iam kind of lost.
Do any of you guys know how to achieve this?
Thanks
search.html
{% extends "base_generic.html" %}
{% block content %}
<h3>Welcome to search information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
{% endblock %}
index.html
{% block content %}
<h3>Welcome to information Repository</h3>
<form method="post">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
just for clarify things more, ill add the forms.py too
from django import forms
from .models import WebsiteRepository
class SearchForm(forms.Form):
websiterepository = WebsiteRepository
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
class SelectTypeForm(forms.Form):
OPTIONS = (('1', 'Envirnmental Indicators'),('2','Economic Indicators'),('3','Social Indicators'),)
types = forms.ChoiceField(choices=OPTIONS)

Your code is wrong on many points.
First thing first: for a search, you want a GET request, not a POST (POST is for updating the server's state - adding or updating your database mostly). This is the semantically correct method (since you want to GET data), and it will allow a user to bookmark the url.
Second point: you don't want to submit the search form to the index view but to the search view. No need for redirects etc, just use the {% url %} templatetag to fill the action attribute of your form (you of course need to have a 'Search' url in your urls.py):
<form method="get" action="{% url 'Search' %}">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>
if you want to have this form on more than one page (which is often the case for search forms), use an inclusion tag tha will take care of creating an unbound SearchForm and render the template fragment.
Then in your search view, you only want GET requests, and do not use two different templates, this will only lead to useless duplication.
def Search(request):
form = SearchForm(request.GET)
# use the form's data - if any - to get search results
# and put those results (even if empty) in you context
return render(request, 'reply.html', {'env_dict':env_dict})
And finally, your search form is totally broken:
class SearchForm(forms.Form):
# this is totally useless
websiterepository = WebsiteRepository
# this will only be evaluated once at process startup, so you will
# get stale data in production - and probably different data
# per process, in a totally unpredictable way.
# You need to either put this in the form's __init__ or wrap it
# in a callable and pass this callable
env_indicators = websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
indicator = forms.ChoiceField(choices=env_indicators,label = 'Indicator' )
# are you going to manually add a new year choice every year ???
OPTIONS = (('2000','2000'),('2001','2001'),('2002','2002'), ('2003','2003'),('0000','0000'),)
year = forms.ChoiceField(choices=OPTIONS)
For the "indicators" ChoiceField you want something like:
def get_indicators_choices():
return Websiterepository.objects.filter (key_aspect='Environmental').values_list('repo_id','indicator')
class SearchForm(forms.Form):
# IMPORTANT : we are NOT calling the function here, just
# passing it (python functions are objects) to the field, which
# will call it everytime the form is instanciated, so you don't
# have stale data
indicator = forms.ChoiceField(
choices=get_indicator_choices,
label='Indicator')
As a last note: be consistent with your namings (ie why name one view in all lower (index) and capitalize the other (Search) ? Whichever convention you choose (I strongly suggest respecting pep8 here), at least stick to it for the whole project.

The problem is that code is not redirecting to /search, instead rendering search.html after post from index.html.
Try doing like-
views.py-
#your code
def index(request):
#do something
if request.method == 'POST':
return redirect('Search')
else:
#render index.html
def search(request):
#do something
if request.method == 'POST':
#render reply.html
else:
#render search.html
Another way to achieve this is if you specify action in your form so that form posts on /search.
search.html
<form method="post" action="/search">
{% csrf_token %}
{{form.as_p}}
<button type = 'submit'>submit</button>
</form>

Related

Creating url out of user's choices from the form. Django gives NoReverseMatch, saying i need at least two arguments and i have none

So basically i have form on my homepage that asks users to choose two cities : where they are now, and where they want to go. I display all the available options with ModelChoiceField() easily, but when i try to use user's choices to make arguments for url, i get NoReverseMatch. I did a little research and found out due to the fact that at the time when page is loaded, user hasn't chosen anything, so there are no arguments. After that, i took different approach - i tried to set /search/ as url for the form. There, i extracted user's choices and tried to redirect back to the main url with these two arguments. Error still persists
Traceback Url :
http://dpaste.com/34E3S2V
Here's my forms.py :
class RouteForm(forms.Form):
location = forms.ModelChoiceField(queryset=Location.objects.all())
destination = forms.ModelChoiceField(queryset=Destination.objects.all())
Here's my template :
<p> From where to where ? </p>
<form action="{% url 'listings:search' %}" method="POST">
{{ form.as_p }}
{% csrf_token %}
<input type="submit" value="Let's go!">
</form>
My urls.py :
urlpatterns = [
path('', views.index, name="index"),
path('<location>/<destination>', views.route, name="route"),
path('search/', views.search, name="search")
]
and views.py :
def index(request):
form = forms.RouteForm()
listings = Listing.objects.all()
context = {"listings" : listings, "form" : form }
def route(request, location, destination):
current_location = Location.objects.get(city=location)
future_destination = Destination.objects.get(city=destination)
context = {"current_location" : current_location, "future_destination" : future_destination}
return render(request, 'listings/route.html', context)
def search(request, location, destination):
chosen_location = Location.objects.get(pk=request.POST['location'])
chosen_destination = Destination.objects.get(pk=request.POST['destination'])
return HttpResponseRedirect(reverse('listings:route', args=[chosen_location, chosen_destination]))
What am i missing?
You need to show the full traceback in your question. Actually, the code you've shown wouldn't give that error; instead you would get a TypeError for the search view.
Nevertheless, you have quite a few things wrong here.
Firstly, you need to decide how you want to represent those fields in the URL. You can't just put a Location object in a URL. Do you want to use numeric IDs, or string slugs? Assuming you want to use slugs, your URL would be:
path('<slug:location>/<slug:destination>', views.route, name="route"),
Secondly, you shouldn't have location and destination as parameters to the search function. They aren't being passed in the URL, but you in the POST data.
Next, you need to actually use the Django form you've defined, and get the values from that form cleaned_data. Using the form - in particular calling its is_valid() method - ensures that the user actually chooses options from the fields. So the search function needs to look like this:
def search(request):
if request.method == 'POST':
form = RouteForm(request.POST)
if form.is_valid():
chosen_location = form.cleaned_data['location']
chosen_destination = form.cleaned_data['destination']
return HttpResponseRedirect(reverse('listings:route', args=[chosen_location.city, chosen_destination.city]))
else:
form = RouteForm()
return render(request, 'search.html', {'form': form})

Django: Unable to open a detail view by URL, which causes reverse argument errors in another view

Python 3.5.1
Django 1.10
Been tearing my hair out for hours on this, but have my Reverse Argument error pinned down to the actual problem.
When I try to open a form to edit a particular record in my model, it only opens a blank (unconnected) form. Using the same logic, I am able to delete a record, so I'm sure this is something stupid-simple. But I'm too many hours into this, so I would appreciate a lifeline.
From models.py
class CmsSpaces(models.Model):
sid = models.AutoField(db_column='SID', primary_key=True)
section = models.CharField(db_column='Section', max_length=5)
...Many more columns...
def __unicode__(self):
return self.name
def get_absolute_url(self):
return reverse('cms_spaces:space_edit', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_delete', args = (self.sid), kwargs=None)
return reverse('cms_spaces:space_new', args = None, kwargs = None)
class Meta:
managed = False
db_table = 'cms_spaces'
From views.py
def CmsSpaces_update(request, sid,
template_name='space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
return render(request, template_name, {'form':space_form})
def CmsSpaces_delete(request, sid,
template_name='space_delete.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
if request.method=='POST':
space.delete()
return redirect('space_list')
return render(request, template_name, {'object':CmsSpaces})
From urls.py
from django.conf import settings
from django.conf.urls import include, url
from django.contrib import admin
from cms_spaces import views
urlpatterns = [
url(r'^space_list/$', views.CmsSpaces_list, name='space_list'),
url(r'^space_new/$', views.CmsSpaces_create, name='space_new'),
url(r'^space_edit/(?P<sid>[\w-]+)/$', views.CmsSpaces_update, name='space_edit'),
url(r'^space_delete/(?P<sid>[\w-]+)/$', views.CmsSpaces_delete, name='space_delete'),
]
From space_edit.html. When I enter the url directly for .../space_delete/12345, it does proceed to delete the record with sid=12345.
<form method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Add or Update Space" />
</form>
From space_list.html (which throws "Reverse for 'space_edit' with arguments '(10256,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []") as an error. 10256 is in fact an sid for one of the records in the table. When I remove the links to space_edit and space_delete, it does render the list of records. Yes, there are many more lines of code that handle presentation, but this is the boiled down version, and it is "broken" even at this level.
<ul>
{% for CmsSpaces in space_list %}
<li>{{CmsSpaces.sid}} {{ CmsSpaces.section }} {{ CmsSpaces.space}} {{ CmsSpaces.lot }} {{ CmsSpaces.deednum}}
edit
delete
</li>
{% endfor %}
</ul>
All I want to do is be able to call and edit a record. If I can do that, I believe the list view will work, and then I'm well on my way to home free. The issue seems to be that even though it is correctly capturing the sid, it is failing to correctly pass it as an argument to the model. Help! Thanks.
EDIT
from django import forms
from django.forms import ModelForm
from masterdata.models import CmsSpaces
class space_form(ModelForm):
class Meta:
model = CmsSpaces
fields = [
'sid',
'section',
'space',
'lot',
'deednum',
'purchdate',
'notes',
'perpetualcare',
]
Okay... first off a big thanks to #knbk for getting me on track with the list issue.
Second, my initial diagnosis in the OP was wrong. The issue was in my CmsSpaces_update view. Boiled down, the view had the pieces to tie the form to the record, but no actual code to do so. slaps head
So because someone else may someday read this and wonder the same thing...
def CmsSpaces_update(request, sid,
template_name='templates/space_edit.html'):
space = get_object_or_404(CmsSpaces, sid=sid)
form = space_form(request.POST or None, instance=space)
if form.is_valid():
form.save()
return redirect('space_list')
ctx = {}
ctx['form'] = form
ctx['space'] = space
return render(request, template_name, ctx)

django csrf_token in search result url

Have csrf in search result url. Don't know why is there and how to remove it. Search works nice. Here is URL
/search/?csrfmiddlewaretoken=675d1340034e094866d15a921407e3fc&q=testing
here is view:
def search(request):
query = request.GET.get('q', '')
rezult = []
if query:
qset1 = (
Q(title__icontains=query)
)
result = Product.objects.filter(qset1).distinct()
if result.count() == 1:
return HttpResponseRedirect(result[0].get_absolute_url())
return render_to_response('search/search.html',{'query': query, 'result': result, },context_instance=RequestContext(request))
Thanks
Remove {% csrf_token %} from your form in the template, you don't need it since you're making a GET request.
you added {% csrf_token %} in your form. if you dont need csrf remove this from your form and add csrf_exempt.
look at this sample of django:
from django.views.decorators.csrf import csrf_exempt
#csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
I would assume that you've added the {% csrf_token %} within one of the search form's input element. That would cause the token to be submitted along with the form.
Check your search form template.

Add form returns None as record id, causing URL to error

I'm using the code found here (SO.com) to use the same template to both add and edit a record, but when I add a new record and click Submit, I get a 404 on the URL http://192.168.1.3:5678/app/student/edit/None/, and I'm not exactly sure why.
Here is the relevant portion of my urls.py:
url(r'^app/lesson/new/$', 'edit_lesson', {}, 'lesson_new'),
url(r'^app/lesson/edit/(?P<id>\d+)/$', 'edit_lesson', {}, 'lesson_edit'),
Here is the relevant portion of my views.py:
def edit_lesson(request, id=None, template_name='lesson_edit_template.html'):
if id:
t = "Edit"
lesson = get_object_or_404(Lesson, pk=id)
stu = get_object_or_404(Student, pk=sid)
if stu.teacher != request.user:
raise HttpResponseForbidden()
else:
t = "Add"
lesson = Lesson()
if request.POST:
form = LessonForm(request.POST, instance=lesson)
if form.is_valid():
form.save()
# If the save was successful, redirect to another page
return view_lessons(request)
else:
form = LessonForm(instance=lesson)
return render_to_response(template_name, {
'form': form,
't': t,
'lesson': lesson,
}, context_instance=RequestContext(request))
And finally, here is my template:
<h1>{{ t }} Lesson</h1>
<form action="/app/lesson/edit/{{ lesson.id }}/" method="post"> {% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
I'm certain that I'm missing something really easy, but I can't seem to put my finger on it. I'm using Django 1.3.1 if that makes any difference.
Thanks,
MC
There's no need to specify any URL in the form's action attribute. Just do
<form action="" method="post">
and it will POST back to the URL that you originally used to access it, which is what you want.
In add case {{ lesson.id }} is None, because lesson is unsaved Lesson() instance, without pk, so your form is being fired to nonexistent URL.
I recommend separating create and edit views and processing them in different ways (or even inherit generic views - with new class-based generic views it's easy and pleasant).
Also, use {% url %} template tag everywhere instead of hard-coded urls.

Django: issue with a simple form

I'm new at django and I'm having problems with a simple form POST.I have a ModelForm in forms.py and when user enters information in html, views.py taks it and saves it. However, I keep getting an error saying it can't find the view doesn't exist in view.py. Please help me find the error. Thank you!
urls.py
urlpatterns = patterns('',
(r'^mypage/(?P<username>\w+)/$', 'recipeapp.views.my_view'),
forms.py
class NewRecipeForm(forms.ModelForm):
user_info = forms.ForeignKey(User)
title = forms.CharField(min_length=2,max_length=50,required=True,)
post_date = forms.DateField(auto_now=True)
ingredients = forms.TextField(widget=forms.Textarea(),)
picture = forms.ImageField(upload_to='photos/%Y/%m/%d',)
content = forms.TextField(widget=forms.Textarea(),)
views.py
#csrf_protect
from recipeapp.forms import NewRecipeForm
def my_view(request,username):
if request.method == 'POST':
form = NewRecipeForm(request.POST)
if form.is_valid():
form.save()
else:
form = NewRecipeForm()
return render_to_response('postlogin.html',{'username':username},{'form': form}, RequestContext(request))
postlogin.html
<form action="" method="post" id="form">
{% csrf_token %}
<div id="dish-name">
<label><p>Dish name</p></label>
{{form.title}}
</div>
<div id="ingredients">
<label><p>Ingredients</p></label>
{{form.ingredients}}
</div>
<div id="content">
<label><p>Content</p></label>
{{form.content}}
</div>
{{form.picture}}
</form>
Is that really your whole views.py? You have at least three issues:
Firstly, you haven't imported csrf_protect - like any name, a decorator needs to be defined before you can use it.
Secondly, you have to decorate an actual function, not a file. The decorator should go just before the function definition for my_view.
Thirdly, your indentation is broken - the def should not be indented at all.
Given all those, I expect that Python is failing to import your views because of syntax errors.
Also note that you shouldn't really use csrf_protect - you should enable CSRF protection in your middleware (it's on by default) and only use the csrf_exempt decorator, and then only on very very rare occasions.