somthing's wrong with django messages - django

I have a view function like this:
#login_required
def delete_deal(request, id_deal):
id_deal = int(id_deal)
user = get_object_or_404(User, id=request.user.id)
user_deals = Deal.objects.filter(user=user)
if not user_deals.exists():
messages.add_message(request, messages.INFO,
'You have no deal.')
return redirect('index')
return render(request, 'booking/confirmation_delete.html', {'title': title})
If I test with a user who does not have a deal the message is not displayed the first time but if I redo the message is displayed 2 times, what is wrong ?
I have another view function which returns a message and it displays it normally !!
If you need more info tell me
Update:
Here is my url.py of the application where the view function above is located:
urlpatterns = [
url(r'^create_deal/$', views.create_deal, name='create_deal'),
url(r'^delete_deal/(?P<id_deal>[0-9]+)/$', views.delete_deal, name='delete_deal'),
]
The view.py function:
def index(request):
all_deals = Deal.objects.all()
return render(request, 'base.html', locals())
template.html:
{% if messages %}
{% for message in messages %}
<div>{{ message }}</div>
{% endfor %}
{% endif %}

It looks like it adds the message both times and will redirect to 'index' both times... So perhaps the problem is either in the urls and view for 'index' or somehow in the template being rendered. Show us the urls.py, the associated view function/opject and template.

I had this issue because to display the message I tried to enter in the search bar an url towards a deal which does not exist, and looking in the console I saw that passing the url in the search bar the view is executed before even hitting enter
My url was something like this:
http://127.0.0.1:8000/booking/delete_deal/65/
and the number 65 is an id_deal which does not exist
I tried to add a link that redirect to the view function with an id_deal which does not exist and the message is always shown

Related

django, views direct to a another html page

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>

django redirect to another view with context

I have this in my view
def foo(request):
context['bar'] = 'FooBar'
return redirect('app:view')
is there some way to include this context['bar'] when I redirect to 'app:view'? My last resort and alternative would be to do use render() however, I do not want to define all the context variables again. Is there any other approach to this?
I would use session variables in order to pass some context through a redirect. It is about the only way to do it outside of passing them as part of the url and it is the recommended django option.
def foo(request):
request.session['bar'] = 'FooBar'
return redirect('app:view')
#jinja
{{ request.session.bar }}
A potential pitfall was pointed out, whereas the session variable gets used incorrectly in a future request since it persists during the whole session. If this is the case you can fairly easily circumvent this problem in a future view in the situation it might be used again by adding.
if 'bar' in request.session:
del request.session['bar']
In django You can not pass parameters with redirect. Your only bet is to pass them as a part of URL.
def foo(request):
context['bar'] = 'FooBar'
redirect(reverse('app:view', kwargs={ 'bar': FooBar }))
in your html you can get them from URL.
you need to use HttpResponseRedirect instead
from django.http import HttpResponseRedirect
return HttpResponseRedirect(reverse('app:view', kwargs={'bar':FooBar}))
I was with the same problem. I would like to redirect to another page and show some message, in my case, it's a error message. To solve it, I used the django messages: https://docs.djangoproject.com/en/4.0/ref/contrib/messages/
I did this:
def foo(request):
message.error(request, 'bla bla bla')
return redirect('foo_page')
In my template foo_page.html:
{% if messages %}
{% for message in messages %}
<div class={{ message.tags }}>{{ message }}</div>
{% endfor %}
{% endif %}
What most work for me is:
next_url = '/'
url = reverse('app:view')
url = f'{url}?next={next_url}&'
return redirect (url)
I just had the same issue, and here is my solution.
I use Django messages to store my parameter.
In template I do not read it with template tag {% for message in messages %}, but rather do POST request to my API to check if there are any messages for me.
views.py
def foo(request):
messages.success(request, 'parameter')
return redirect('app:view')
api/views.py
#api_view(['POST'])
#login_required
def messageList(request):
data = {}
messageList = []
storage = messages.get_messages(request)
for message in storage:
msgObj = makeMessage(message.message, tag=message.level_tag)
messageList.append(msgObj['message'])
data['messages'] = messageList
return Response(data)

Proper way of using url patterns

I've created a form which by submit uploads an item to the database. The problem is that if I press f5 it'll submit the form again, because of the URL is now different.
I have these two url patterns
urlpatterns = [
url(r'(?i)^CMS/$', views.CMS, name='CMS'),
url(r'^createItem/$', views.createItem, name='createItem')
]
and my view looks like this
def CMS(request):
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
def createItem(request):
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
pass
form = itemCreateForm()
context = {
'form' : form,
'message' : 'ItemCreated!'
}
return render(request, 'CMS.html', context)
the CMS.html
{% if message %}
{{ message }}
{% endif %}
<div class='newItemFields'>
<form action="{% url 'kar:createItem' %}" method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
my form
class itemCreateForm(ModelForm):
class Meta:
model = item
fields = ['name', 'type', 'price']
I start at homepage/CMS/ and fill in the form and press submit, and view function createItem runs and creates and saves the object in the database. And sends the user to homepage/CMS/createItem. And now everytime the user press f5 the createItem function will run again and insert another object into the database with the same values as the previous one, even though the input fields are empty (can't wrap my head around that).
I also twice write form = itemCreateForm() which I believe is dubious?
What I'd like to do is after createItem is run, it should send the user back to homepage/CMS/ and not homepage/CMS/createItem. Would that be the proper way to do it? Or is there a smart way of doing this.
At the end of your createItem function, you are rendering HTML of the page rather than redirecting. Instead, you need to do
return HttpResponseRedirect(reverse('kar:index'))
You will need to import HttpResponseRedirect and reverse which is used to resolve the URL through its name.
Check this out: https://docs.djangoproject.com/en/1.10/topics/forms/#the-view
What I'd like to do is after createItem is run, it should send the
user back to homepage/CMS/ and not homepage/CMS/createItem. Would that
be the proper way to do it? Or is there a smart way of doing this.
That would indeed be the proper and smart way to do it. Have one view handle both GET and POST and then redirect after successful form submission. This ensures that the user can't resubmit the form merely by refreshing. And you address your concern about repeating your code.
urlpatterns = [
url(r'(?i)^$', views.index, name='index'),
url(r'^createItem/$', views.createItem, name='createItem')
]
Then combine your views
def createItem(request):
if request.method == 'POST':
f = itemCreateForm(request.POST)
if f.is_valid():
f.save()
return HttpResponseRedirect('/homepage/CMS/')
else :
form = itemCreateForm()
context = {
'form' : form,
'message' : 'Content Manage Site'
}
return render(request, 'CMS.html', context)
Note that the code is now shorter, it gives proper feedback to the user when the form is not valid. And you can't refresh to submit the for twice. We need a small change to the template
<div class='newItemFields'>
<form action=method="POST">
{% csrf_token %}
{{ form.as_p }}
<input type="submit">
</form>
</div>
The message display part isn't needed anymore

Restrict access to Django template

I need to restrict access to a template in Django. This is the scenario:
A guest user uses a form
If the form is validated and fine send the user to the example.com/success/ url.
If the guest user tries to send that link example.com/success to a friend. The friend will see that page as a 404.
I have no clue how to achieve this. Any ideas?
Instead of going to a different URL (/success/), you could just show something different when the form was properly filled. For example, in your view:
def my_view(request, ...):
form = ...
show_success = False
if ... post method ...:
if form.is_valid():
... save etc. ...
show_success = True
return render(request, ..., {'show_success': show_success})
In your template:
{% if show_success %}
Success message here
{% else %}
Form here
{% endif %}

Search result fails with pagination

In my django project I'm using Django Endless Pagination for pagination and haystack + elasticsearch for searching. When I search a specific content the request method is POST and the result is correct, but when I try to paginate through the search result, next request is received as GET and the search result is lost and the whole content is iterated.
Here is my code:
views.py
#login_required(login_url="/")
#page_template('students/students_listing_block.html')
def students(request, template='students/students_listing.html', extra_context=None, *args, **kwargs):
sqs = SearchQuerySet().models(Student)
if request.POST:
searchcontent = request.POST.get('content', None)
if searchcontent:
sqs = sqs.filter(content=searchcontent)
students = sqs.order_by('-created_at')
context = {
'students': students,
}
if extra_context is not None:
context.update(extra_context)
return render_to_response(template, context,
context_instance=RequestContext(request))
and my template
{% load endless %}
{% lazy_paginate students %}
{% for student in students %}
// Do the displaying here
{% endfor %}
{% show_more %}
It is get request because the standard request method for search is GET. My recommendation is to change the request method for search into GET.
Or, if you want to keep using POST, you need to change the request method for every page link. This is a good library to change the hyperlink request method. https://github.com/rails/jquery-ujs
TEST