Django adding a feedback form on every page - django

I have a form view for user feedback:
urls.py:
url(
r'^feedback/$',
'tool.views.Feedback',
name='feedback'
),
url(
r'^thanks/$',
direct_to_template, {
'template': 'tool_feedback_thanks.html'
},
name='feedback_thanks'
),
forms.py:
class FeedbackForm(forms.Form):
yes_no = forms.ChoiceField(
choices=YES_NO_CHOICE,
initial=1,
widget=forms.RadioSelect(attrs={'class': 'can_reveal_input'}),
label="Are you happy with Our service?"
)
comments = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'hidden', 'placeholder': 'Leave us your comments...'
}),
required=False,
label=""
)
views.py:
def Feedback(request,
template_name='tool_feedback.html'):
title = u'Leave us some feedback'
form = FeedbackForm(request.POST or None)
if form.is_valid():
yes_no = form.cleaned_data['yes_no']
comments = form.cleaned_data['comments']
sender = "A Unirac website user"
recipients = ['person#example.com']
send_mail(yes_no, comments, sender, recipients)
return HttpResponseRedirect(
reverse('feedback_thanks')
)
return render_to_response(template_name, {
'title': title,
'form': form,
}, RequestContext(request))
This works a treat, but now the client is asking that this form be included on every single page. I guess the form can be submitted via js to the appropriate url, but what is the best way to include the unbound form on every page?
Any help would be much appreciated.

I'd create a context processor, to include the form in every view.
EDIT:
To get the user to the previous URL he/she was browsing, you can use just URLs.
# yourapp/context_processors.py
def feedback_form_context_processor(request):
return {
'feedback_form': FeedbackForm(),
'feedback_form_url': reverse("feed_app:form_process", args=(request.path))
}
This is how urls.py could look like:
urlpatterns = patterns('feed_app.views',
url(r'^process-feedback-form/(?P<next_url>\d+)', 'form_process', name='form_process'),
)
And the view for the form:
def form_process(request, next_url):
# Process form, do your stuff here
# if its valid redirect to the url
return redirect(next_url)
And you should structure your templates to have the correct layout. For example, having a base template:
# templates/base.html
<html>
<body>
..
{% block maincontent %}
{% endblock %}
..
{# The form!!! #}
<form action='{{feedback_form_url}}' method='POST'>
#csrftoken
{{ feedback_form.as_p }}
</form>
</body>
</html>
To create a simple view just use the correct template.
# templates/just_a_random_view.html
{% extends base.html %}
{% block maincontent %}
<h1>Content!</h1>
{% endblock %}
Finally, include it in your settings:
# settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
...
"yourapp.context_processors.feedback_form_context_processor"
)

I believe that easiest way to include form would be to use a assignment_tag:
In template library:
#register.assignment_tag
def feedback_form(format_string):
return FeedbackForm()
In template
{% feedback_form as form %}
{# display form... %}
{{ form.as_p }}

To add on to #bmihelac, who's answer worked really well for me. Since django 2.0 assignment_tag is deprecated in favor of simple_tag. So you can pretty much follow his answer exactly by replacing assignment_tag with simple_tag, like so:
from django import template
from .forms import FeedbackForm
register = template.Library()
#register.simple_tag
def feedback_form():
return FeedbackForm()
And then just refer to https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#code-layout for info about how to import it into the template!

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>

Reverse error in Django 1.10

I'm new to Django and slowly learning how it works. I just upgraded to 1.10 and part of my app stopped working. I know it is related to the changes made into Reverse. I have been reading and I cannot find exactly what I'm doing wrong. Almost everything works as it should with a couple of exceptions. The behavior is as follows:
1) On my app I load reservations/create, it works perfectly I can create my reservation
2) When I click create, the reservation is actually created and saved into the database, but the browser is sent to the wrong address. It gets sent to reservations/create instead of reservations/reservation number (for example reservations/2 where it shows its details) and shows a Reverse error (included in this post)
3) If I test reservations/2 for example, it shows that it was actually created.
4) Also if a go straight to reservations/ it should show a list of all the ones already create, but instead shows a Reverse error too.
I would really appreciate any help in understanding what I'm doing wrong.
Models.py
class Reservation(models.Model):
res_number = models.AutoField(primary_key=True)
date = models.DateField(default=datetime.date.today())
status = models.CharField(max_length=10,default="Created")
reservation_type = models.CharField(max_length=11,choices=shced_type_choices, default="rental")
aircraft = models.ForeignKey('aircraft.Aircraft')
renter = models.CharField(max_length=30,blank=False,null=False)
instructor = models.CharField(max_length=30,blank=True,null=False)
def get_absolute_url(self):
return reverse("reservations:detail", kwargs={"res_number": self.res_number})
Main urls.py
url(r'^reservations/', include('dispatch.urls', namespace='reservations')),
Dispatch.urls
from django.conf.urls import include, url
from django.contrib import admin
from .views import (
reservations_list,
reservations_detail,
reservations_edit,
reservations_dispatch,
reservations_close,
reservations_cancel,
reservations_create,
reservations_close,
)
urlpatterns = [
url(r'^$', reservations_list),
url(r'^(?P<res_number>\d+)/$', reservations_detail),
url(r'^(?P<res_number>\d+)/edit/$', reservations_edit),
url(r'^(?P<res_number>\d+)/dispatch/$', reservations_dispatch),
url(r'^(?P<res_number>\d+)/close/$', reservations_close),
url(r'^(?P<res_number>\d+)/cancel/$', reservations_cancel),
url(r'^create/$', reservations_create),
url(r'^close/$', reservations_close),
]
Views.py
from django.contrib import messages
from django import forms
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from .forms import ReservationForm, CloseReservationForm
from .models import Reservation
def reservations_list(request):
queryset = Reservation.objects.all()
context = {
"object_list": queryset,
"title": "List of Reservations:"
}
return render(request, "dispatch/list.html", context)
def reservations_detail(request, res_number=None):
instance = get_object_or_404(Reservation, res_number=res_number)
context = {
"title": instance.renter,
"instance": instance,
}
return render(request, "dispatch/details.html", context)
def reservations_create(request):
form = ReservationForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
print(instance.aircraft.hobbs)
instance.save()
messages.success(request, "Reservation Created")
return HttpResponseRedirect(instance.get_absolute_url())
context = {
"form": form,
}
return render(request, "dispatch/create.html", context)
Details.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | {{instance.res_number}}{% endblock head_title %}
{% block content %}
<h1>Reservation for {{title}} on {{instance.date}}</h1>
Reservation Number: {{instance.res_number}}</br>
Date: {{instance.date}}</br>
Status: {{instance.status}}</br>
Reservation Type: {{instance.reservation_type}}</br>
Aircraft: {{instance.aircraft}}</br>
Renter's Name: {{instance.renter}}</br>
Instructor's Name: {{instance.instructor}}</br>
Expected Flight Hours: {{instance.expected_hours}} Hrs</br>
Actual Flown Hours: {{instance.flown_hours}} Hrs</br>
Reservation Created on: {{instance.created}}</br>
Last Updated on: {{instance.updated}}</br>
{% endblock content %}
Create.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | Create{% endblock head_title %}
{% block content %}
<h1>Create Reservation</h1>
<form method='POST' action=''>{% csrf_token %}
{{form.as_p}}
<input type="submit" name="Create Reservation">
</form>
{% endblock content %}
Reverse error screenshot
Your problem is your routes don't have names. So when you are using reverse('some_name'), you have to have such name defined. The name is detail in your case, so you want to do something like this (see the parameter name)
urlpatterns = [
url(r'^(?P<res_number>\d+)/$', reservations_detail, name='detail'),
]
Also please don't insert traceback as a screenshot. You see the link 'switch to copy-and-paste view'? Yeah, use that the next time.

user is authenticated in profile page(redirected to profile page), but not logged in different pages

I'm using Userena, I set everything that I can register/login/change my profile..etc but as soon as I leave the profile page I'm automatically logged out or seems like never logged in.
seems like there's something wrong in this maybe?
this is from base.html, I pass that to index html but inside index html, not logged in.
{% load static %}
{% load i18n static %}
{% load url from future %}
{% if user.is_authenticated %}
<li>Logout</li>
<li>Add a new Category</li>
{% else %}
<li>Register Here</li>
<li>Login</li>
{% endif %}
I have no idea where to look at, please help
my views.py for index
#for front page
def index(request):
categories = Category.objects.order_by('likes')[:5]
latest_posts = Post.objects.all().order_by('-created_at')
popular_posts = Post.objects.all().order_by('-views')
hot_posts = Post.objects.all().order_by('-score')[:25]
t = loader.get_template('main/index.html')
context_dict = {
'latest_posts' :latest_posts,
'popular_posts' :popular_posts,
'hot_posts' :hot_posts,
'categories':categories
}
c = Context(context_dict)
return HttpResponse(t.render(c))
Including {{ user }} or {% user.is_authenticated %} in your template will only work if the user is included in the template context. You can either add it explicitly in your view,
context_dict = {
'latest_posts': latest_posts,
'popular_posts': popular_posts,
'hot_posts': hot_posts,
'categories': categories,
'user': request.user,
}
c = Context(context_dict)
return HttpResponse(t.render(c))
or you can use the render shortcut, which takes care of loading the template and rendering it for you.
from django.shortcuts import render
def index(request):
categories = Category.objects.order_by('likes')[:5]
latest_posts = Post.objects.all().order_by('-created_at')
popular_posts = Post.objects.all().order_by('-views')
hot_posts = Post.objects.all().order_by('-score')[:25]
context_dict = {
'latest_posts': latest_posts,
'popular_posts': popular_posts,
'hot_posts': hot_posts,
'categories': categories
}
return render(request, 'main/index.html', context_dict)
If you use the render shortcut, you need to make sure that the 'django.contrib.auth.context_processors.auth', is included in your context_processors setting (it's included in the default generated settings file).

Django. Crispy forms. showing error messages with crispy filter and customizing them

I am new to django forms and Crispy Forms. I have some simple forms in a little forum Im developing. I think I don't need to use the %crispy% tag. I only need the form|crispy filter. However, I don't know why they don't render the error messages.
Also, if I want to customize the error messages (they must be in spanish), do I need to use the %crispy% tag or is it possible to do this with the |crispy filter?
Anyway, here is one of my forms:
from django import forms
from django.forms import Textarea
class FormNuevoVideo(forms.Form):
url = forms.URLField(initial='http://', max_length=250)
titulo = forms.CharField(max_length=150)
descripcion = forms.CharField(
help_text="...",
widget=Textarea(attrs={'rows': 3, 'data-maxlength': 500}))
Here is the view:
#login_required
def nuevo_video(request, slug):
template = 'videos/nuevo.html'
tema = Temas.objects.get(slug=slug)
if request.method == 'POST':
form = FormNuevoVideo(request.POST)
if form.is_valid():
...
nuevo_video.save()
return redirect('videos:videos_tema', slug=tema.slug, queryset='recientes')
else:
return redirect('videos:nuevo_video', slug=tema.slug) #this same view.
else:
form_nuevo_video = FormNuevoVideo()
context = {'form_nuevo_video': form_nuevo_video, 'tema': tema}
return render(request, template, context)
And in the HTML:
{% block form %}
<form action = "{% url 'videos:nuevo_video' tema.slug %}" method = "post">
{% csrf_token %}
{{form_nuevo_video|crispy}}
<input class = "btn pull-right" type = "submit" value ="enviar"/>
</form>
{% endblock form %}
So, lets say, when someone tries to submit a video with a title of more than 150 characters, it doesn't display the error. I am sure I am missing something simple. Also, I'd like to customize the error messages so that they are in spanish. Thanks in advance.

django template inheritance and context

I am reading the definitive guide to django and am in Chapter 4 on template inheritance. It seems that I am not doing something as elegant as should be possible as I am having to duplicate some code for the context to appear when calling the child view. Here is the code in views.py:
def homepage(request):
current_date = datetime.datetime.now()
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals())
def contact(request):
current_date = datetime.datetime.now()
current_section = 'Contact page'
return render_to_response("contact.html", locals())
It seems redundant to have to include the current_date line in each function.
Here is the base html file that homepage calls:
<html lang= "en">
<head>
<title>{% block title %}Home Page{% endblock %}</title>
</head>
<body>
<h1>The Site</h1>
{% block content %}
<p> The Current section is {{ current_section }}.</p>
{% endblock %}
{% block footer %}
<p>The current time is {{ current_date }}</p>
{% endblock %}
</body>
</html>
and a child template file:
{% extends "base.html" %}
{% block title %}Contact{% endblock %}
{% block content %}
<p>Contact information goes here...</p>
<p>You are in the section {{ current_section }}</p>
{% endblock %}
If I don't include the current_date line when calling the child file, where that variable should appear is blank.
You can pass a variable to every template by using a Context Processor:
1. Adding the context processor to your settings file
First, you will need to add your custom Context Processor to your settings.py:
# settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
'myapp.context_processors.default', # add this line
'django.core.context_processors.auth',
)
From that you can derive that you will need to create a module called context_processors.py and place it inside your app's folder. You can further see that it will need to declare a function called default (as that's what we included in settings.py), but this is arbitrary. You can choose whichever function name you prefer.
2. Creating the Context Processor
# context_processors.py
from datetime import datetime
from django.conf import settings # this is a good example of extra
# context you might need across templates
def default(request):
# you can declare any variable that you would like and pass
# them as a dictionary to be added to each template's context:
return dict(
example = "This is an example string.",
current_date = datetime.now(),
MEDIA_URL = settings.MEDIA_URL, # just for the sake of example
)
3. Adding the extra context to your views
The final step is to process the additional context using RequestContext() and pass it to the template as a variable. Below is a very simplistic example of the kind of modification to the views.py file that would be required:
# old views.py
def homepage(request):
current_date = datetime.datetime.now()
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals())
def contact(request):
current_date = datetime.datetime.now()
current_section = 'Contact page'
return render_to_response("contact.html", locals())
# new views.py
from django.template import RequestContext
def homepage(request):
current_section = 'Temporary Home Page'
return render_to_response("base.html", locals(),
context_instance=RequestContext(request))
def contact(request):
current_section = 'Contact page'
return render_to_response("contact.html", locals(),
context_instance=RequestContext(request))
So, you can use django.views,generic.simple.direct_to_template instead of render_to_response. It uses RequestContext internaly.
from django.views,generic.simple import direct_to_template
def homepage(request):
return direct_to_template(request,"base.html",{
'current_section':'Temporary Home Page'
})
def contact(request):
return direct_to_template(request,"contact.html",{
'current_section':'Contact Page'
})
Or you can even specify it directly at urls.py such as
urlpatterns = patterns('django.views.generic.simple',
(r'^/home/$','direct_to_template',{
'template':'base.html'
'extra_context':{'current_section':'Temporary Home Page'},
}),
(r'^/contact/$','direct_to_template',{
'template':'contact.html'
'extra_context':{'current_section':'Contact page'},
}),
For django v1.8+ variables returned inside context processor can be accessed.
1. Add the context processor to your TEMPLATES list inside settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
'your_app.context_processor_file.func_name', # add this line
],
},
},
]
2. Create new file for context processor and define method for context
context_processor_file.py
def func_name(request):
test_var = "hi, this is a variable from context processor"
return {
"var_for_template" : test_var,
}
3. Now you can get the var_for_template in any templates
for example, add this line inside: base.html
<h1>{{ var_for_template }}</h1>
this will render:
<h1>hi, this is a variable from context processor</h1>
for updating templates to django 1.8+ follow this django doc