Flask Passing Form argument to a function - flask

I am new to Flask and I am just trying to pass 4 arguments that I got from my form to another python function. When I submit my form, I am getting this error : "TypeError: search() missing 4 required positional arguments: 'keywords', 'starting_date', 'ending_date', and 'country'"
I verified and all of my variables have data from the form, so they are not empty
`
#app.route('/')
#app.route('/index', methods=["GET", "POST"])
def index():
if request.method == "POST":
keywords = request.form.get('keywords')
starting_date = request.form.get('starting_date')
ending_date = request.form.get('ending_date')
country = request.form.get('country')
return redirect(url_for("search", keywords=keywords, starting_date=starting_date, ending_date=ending_date, country=country))
else:
return render_template("index.html")
#app.route('/search', methods=["GET"])
def search(keywords, starting_date, ending_date, country ):
return render_template('result.html', title='Result')

You'll need to define your search route as
#app.route('/search/<keywords>/<starting_date>/<ending_date>/<country>', methods=["GET"])
to get your current implementation to work, see Variable Rules section in Flask's doc.
This is, however, problematic. For one, you probably don't want such a messy URL.
A better approach is to ask Flask to send your data to search as query parameters and retrieve it through request.args. Redefine search as
#app.route('/search', methods=["GET"])
def search():
keywords = request.args['keywords']
starting_date = request.args['starting_date']
ending_date = request.args['ending_date']
country = request.args['country']
# perform search
return render_template('result.html', title='Result')
Now url_for will generate a URL of the form /index?keywords=<value>&starting_date=<value>....
See this great SO answer for all the ways to get data from a request: https://stackoverflow.com/a/16664376/1661274

You need to render the template with your form parameters.
Try this:
#app.route('/index', methods=["GET", "POST"])
def index():
if request.method == "POST":
keywords = request.form.get('keywords')
starting_date = request.form.get('starting_date')
ending_date = request.form.get('ending_date')
country = request.form.get('country')
return render_template('result.html', keywords=keywords, starting_date=starting_date, ending_date=ending_date, country=country)
else:
return render_template("index.html")

Related

Views are different but rendering same template

i want to use slug field in both url which is
path('<slug:title>/',views.news_read,name="news_read"),
path('<slug:title>/',views.movie_read,name="movie_read"),
but both the url picking same template instead of their template i am trying to create blog site
i don't understand both the url are uniques so why django is picking wrong template
my views for both the url
def movie_read(request, title):
movie = Movie.objects.filter(urltitle=title)
if request.method == 'POST':
form = Commentform(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Thank You For Your Comment')
else:
form = Commentform()
return render(request,'movie_read.html',{'movie':movie,'form':form})
def news_read(request, title):
news = News.objects.filter(urltitle=title)
if request.method == 'POST':
form = Commentform(request.POST)
if form.is_valid():
form.save()
messages.success(request, 'Thank You For Your Comment')
else:
form = Commentform()
return render(request,'news_read.html',{'news':news,'form':form})
but i when do some change like this it work
path('<slug:title>/news',views.news_read,name="news_read"),
path('<slug:title>/movie',views.movie_read,name="movie_read"),
but this doesn't look good any idea what to do solve the issue
Django will "fire" the first path that matches the URL. This thus means that for every slug you enter, news_read will fire.
You can solve this by trying to query both models, and then decide which to use, like:
def my_view(request, title):
is_movie = Movie.objects.filter(urltitle=title).exists()
if is_movie:
return movie_read(request, title)
else:
return news_read(request, title)
def movie_read(request, title):
# …
def news_read(request, title):
# …
and then thus trigger the my_view function if it is a slug:
path('<slug:title>/', views.my_view, name='news-movie-read'),
but a more elegant solution is to simply define two non-overlapping URL patterns:
path('news/<slug:title>/', views.news_read,name='news_read'),
path('movie/<slug:title>/', views.movie_read,name='movie_read'),
This also will prevent clashes where a Movie object and a News object have both the same url_title since in that case, it will always use the movie_read view.
I seems that tou url path is the same, that is why django picking wrong view.
Try to change urls to this:
path('news/slug:title/',views.news_read,name="news_read"),
path('movies/slug:title/',views.movie_read,name="movie_read"),

Using session to store form input values in order to uso in another view

I am storing some form values, which is a list filter, from a post method in the request.session in order to use it in another view function to render the filtered results. The problem is any user that I log in keep the results stored, if they access the results page directly they will see other users filter results.
I use pagination (digg without AJAX), I am using django-el-pagination.
the views.py
def search(request):
if request.method == 'POST':
form = ComprarBuscaForm(request.POST)
if form.is_valid():
cidade = form.cleaned_data['cidade']
request.session['cidade'] = form.cleaned_data['cidade']
request.session['quartos'] = form.cleaned_data['quartos']
request.session['tipo_imovel'] = form.cleaned_data['tipo_imovel']
request.session['preco_minimo'] = form.cleaned_data['preco_minimo']
request.session['preco_maximo'] = form.cleaned_data['preco_maximo']
request.session['area_minima'] = form.cleaned_data['area_minima']
request.session['area_maxima'] = form.cleaned_data['area_maxima']
return HttpResponseRedirect(reverse('imoveis:resultado_busca'))
else:
form = ComprarBuscaForm()
return render (request, 'imoveis/busca_comprar.html', {'form':form})
def search_result(request):
anuncios = Anuncio.objects.filter(quartos=request.session['quartos'],
cidade=request.session['cidade'],
tipo_imovel=request.session['tipo_imovel'],
preco_venda__gte=request.session['preco_minimo'],
preco_venda__lte=request.session['preco_maximo'],
area_construida__gte=request.session['area_minima'],
area_construida__lte=request.session['area_maxima'],
tipo_anuncio='Venda')
return render(request, 'imoveis/resultado_busca_comprar.html', {'anuncios': anuncios})
Everything is working fine although the fact I mentioned before. I am wondering if what I am doing is the right approach for this kind of situation.
Is it really necessary to use two views for this? I would filter and render in the same form view if I were you.
def search(request):
if request.method == 'POST':
form = ComprarBuscaForm(request.POST)
if form.is_valid():
anuncios = Anuncio.objects.filter(quartos=request.session['quartos'],
cidade=request.session['cidade'],
tipo_imovel=form.cleaned_data['tipo_imovel'],
preco_venda__gte=form.cleaned_data['preco_minimo'],
preco_venda__lte=form.cleaned_data['preco_maximo'],
area_construida__gte=form.cleaned_data['area_minima'],
area_construida__lte=form.cleaned_data['area_maxima'],
tipo_anuncio='Venda')
return render(request, 'imoveis/resultado_busca_comprar.html', {'anuncios': anuncios})
else:
form = ComprarBuscaForm()
return render (request, 'imoveis/busca_comprar.html', {'form':form})

How do I filter sensitive Django POST parameters out of Sentry error reports?

To quote the Django docs:
#sensitive_post_parameters('pass_word', 'credit_card_number')
def record_user_profile(request):
UserProfile.create(user=request.user,
password=request.POST['pass_word'],
credit_card=request.POST['credit_card_number'],
name=request.POST['name'])
In the above example, the values for the pass_word and credit_card_number POST parameters will be hidden and replaced with stars (******) in the request’s representation inside the error reports, whereas the value of the name parameter will be disclosed.
To systematically hide all POST parameters of a request in error reports, do not provide any argument to the sensitive_post_parameters decorator:
#sensitive_post_parameters()
def my_view(request):
...
As a test, I added the following code to my Django 1.6 application:
views.py:
#sensitive_post_parameters('sensitive')
def sensitive(request):
if request.method == 'POST':
raise IntegrityError(unicode(timezone.now()))
return render(request, 'sensitive-test.html',
{'form': forms.SensitiveParamForm()})
forms.py:
class SensitiveParamForm(forms.Form):
not_sensitive = forms.CharField(max_length=255)
sensitive = forms.CharField(max_length=255)
When I submit this form via POST, I can see the values of both fields (including sensitive) clear as day in the Sentry report.
What am I doing wrong here? I'm using Django 1.6 and Raven 3.5.2.
Thanks in advance for your help!
Turns out that this stemmed from a bug in Django itself!
If you haven't changed DEFAULT_EXCEPTION_REPORTER_FILTER in your settings file, you get the default filter of SafeExceptionReporterFilter.
If you've used the sensitive_post_parameters decorator, this will result in your calling SafeExceptionReporterFilter's get_post_parameters method:
def get_post_parameters(self, request):
"""
Replaces the values of POST parameters marked as sensitive with
stars (*********).
"""
if request is None:
return {}
else:
sensitive_post_parameters = getattr(request, 'sensitive_post_parameters', [])
if self.is_active(request) and sensitive_post_parameters:
cleansed = request.POST.copy()
if sensitive_post_parameters == '__ALL__':
# Cleanse all parameters.
for k, v in cleansed.items():
cleansed[k] = CLEANSED_SUBSTITUTE
return cleansed
else:
# Cleanse only the specified parameters.
for param in sensitive_post_parameters:
if param in cleansed:
cleansed[param] = CLEANSED_SUBSTITUTE
return cleansed
else:
return request.POST
The problem with the above is that while it will correctly return a QuerySet with the sensitive POST parameters set to CLEANSED_SUBSTITUTE ('********************')...it won't in any way alter request.body.
This is a problem when working with Raven/Sentry for Django, because it turns out that the get_data_from_request method of Raven's DjangoClient first attempts to get the request's POST parameters from request.body:
def get_data_from_request(self, request):
[snip]
if request.method != 'GET':
try:
data = request.body
except Exception:
try:
data = request.raw_post_data
except Exception:
# assume we had a partial read.
try:
data = request.POST or '<unavailable>'
except Exception:
data = '<unavailable>'
else:
data = None
[snip]
The fastest fix turned out to just involve subclassing DjangoClient and manually replacing its output with the cleansed QuerySet produced by SafeExceptionReporterFilter:
from django.views.debug import SafeExceptionReporterFilter
from raven.contrib.django.client import DjangoClient
class SafeDjangoClient(DjangoClient):
def get_data_from_request(self, request):
request.POST = SafeExceptionReporterFilter().get_post_parameters(request)
result = super(SafeDjangoClient, self).get_data_from_request(request)
result['sentry.interfaces.Http']['data'] = request.POST
return result

NoReverseMatch arguments django

Why does this return a NoReverseMatch error?
view:
def browse(request):
thing_list = Thing.objects.all()
if request.method == 'POST':
form = BrowseForm(request.POST)
if form.is_valid():
make = Make.objects.all()
return HttpResponseRedirect(reverse('browse_makes', args=[make]))
else:
form = BrowseForm()
return render(request, 'browse.html', {'form':form, 'thing_list':thing_list})
def makes(request, make):
ad_list = Thing.objects.filter(make=make)
return render(request, 'browse-makes.html', {'thing_list':thing_list})
url:
url(r'^browse/$', 'axlepost.views.browse.browse', name='browse'),
url(r'^browse/(?P<make>[\w-]+)/$', 'axlepost.views.browse.makes', name='browse_makes'),
form:
class BrowseForm(forms.Form):
make = forms.ModelChoiceField(queryset=Make.objects.all())
model:
class Make(models.Model):
make = models.CharField(max_length=20)
class Thing(models.Model):
make = models.ForeignKey(Make)
Returns Reverse for 'browse_makes' with arguments '([<Make: Samsung>],)' and keyword arguments '{}' not found.
Thanks for your ideas on how to solve this error!
You are handing reverse() a QuerySet rather than 1 value. Also its an .all() query so it may well have multiple objects and it's not related to the form result.
make = Make.objects.all() # QuerySet = ([<Make: Samsung>], [<Make: Sony>], [<Make: Apple>], etc)
return HttpResponseRedirect(reverse('browse_makes', args=[make])) # Expects 1 value
Assuming you want to redirect to the Make selected in the form then you'll need something like:
if form.is_valid():
# Get the valid form data
cd = form.cleaned_data
# Get the selected Make
make = cd.get('make')
# Redirect - note 'make.make'. You want to pass the value not the object
return HttpResponseRedirect(reverse('browse_makes', kwargs={'make': make.make}))
I think there are couple of issues
Your urls does not correspond to appropriate view
change this line
url(r'^browse/(?P<make>[\w-]+)/$', 'axlepost.views.browse.makes', name='browse_makes'),
to
url(r'^browse/(?P<make>[\w-]+)/$', 'axlepost.views.browse', name='browse_makes'),
Change your view definition to take make parameter
def browse(request, make):
...
#change your local variable 'make' to something else

django - how to check if model is empty

I have settings form page. If user filled the form once; it must display those values. But if there is no data [first time] I get query error. I need that query, because the form data must be written as related with current user [logged in].
here is my view part :
#login_required(login_url='/login/')
def profile_page(request,username):
query = Profile.objects.get(owner__username = username) ##error!
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
I think I need to check the model and if it is empty I should do something different.
I am stuck.
Thank you
You want to make use of the .exists() queryset option
#login_required(login_url='/login/')
def profile_page(request,username):
form = profile_form()
if Profile.objects.get(owner__username = username).exists():
query = Profile.objects.get(owner__username = username)
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},context_instance = RequestContext(request))
see QuerytSet API reference for more information
You just need to wrap that get query in try ... except and set instance to none, like this.
from django.core.exceptions import ObjectDoesNotExist
#login_required(login_url='/login/')
def profile_page(request,username):
try:
query = Profile.objects.get(owner__username = username)
#to be more specific you can except ProfileObjectDoesNotExist
except ObjectDoesNotExist:
query = None #Doesn't exist, set to None
if request.method == 'POST':
form = profile_form(request.POST,instance=query)
form.save()
return HttpResponseRedirect('/admin/')
else:
form = profile_form(instance=query)
return render_to_response('profile_save.html',{'form':form},
context_instance = RequestContext(request))
I think i may have use get_or_create for this purpose.
Profile.objects.get_or_create(owner__username = username)