I'm trying to create a form where users can add a new topic within a category. Then the users will be able to comment on each topic. Everything was working up until I tried adding the form page for new_topic
The error I receive:
ValueError at /new_topic/
The view speak_space.views.new_topic didn't return an HttpResponse object. It returned None instead.
Heres the code for my view.py and urls.py files:
view.py
from django.shortcuts import render, redirect
from .models import Category
from .models import Topic
from .forms import TopicForm
def index(request):
"""The home page for speak_space"""
return render(request, 'speak_space/index.html')
def categories(request):
"""Show all categories."""
categories = Category.objects.order_by('date_added')
context = {'categories': categories}
return render(request, 'speak_space/categories.html', context)
def category(request, category_id):
"""Show a single category(tribe) and all of its topics(convos)."""
category = Category.objects.get(id=category_id)
topics = category.topic_set.order_by('-date_added')
context = {'category': category, 'topics': topics}
return render(request, 'speak_space/category.html', context)
def topics(request):
"""Show all topics within a category(tribe)."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'speak_space/topics.html', context)
def topic(request, topic_id):
"""Show a single topic(convo/post) and all of it's replies."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'speak_space/topic.html', context)
def new_topic(request):
"""Add a new conversation topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('speak_space:topics')
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
and heres my url file:
from django.urls import path
from . import views
app_name = 'speak_space'
urlpatterns = [
# Home page
path('', views.index, name='index'),
# Page that shows all categories
path('categories/', views.categories, name='categories'),
# Profile page for a category
path('categories/<int:category_id>/', views.category, name='category'),
# Page that shows all topics.
path('topics/', views.topics, name='topics'),
# Detail page for a single topic(conversation)
# Where you go after you click on someones post
path('topics/<int:topic_id>/', views.topic, name='topic'),
# Page for adding a new conversation topic
path('new_topic/', views.new_topic, name='new_topic'),
]
and my forms page:
from django import forms
from .models import Topic
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
Your new_topic view doesn't return anything in case of GET request. You shoud make return statement common:
def new_topic(request):
"""Add a new conversation topic."""
if request.method != 'POST':
# No data submitted; create a blank form.
form = TopicForm()
else:
# POST data submitted; process data.
form = TopicForm(data=request.POST)
if form.is_valid():
form.save()
return redirect('speak_space:topics')
# The changes are here, now return will work for all requests types POST and GET
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
Deindent the following lines
# Display a blank or invalid form.
context = {'form': form}
return render(request, 'speak_space/new_topic.html', context)
inside your new_topic method.
Related
there is a small django project that has methods for creating a topic, creating and editing posts within it.
I don't understand how to write a method to delete a post in a topic
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
from django.http import Http404
#login_required()
def new_entry(request, topic_id):
topic = Topic.objects.get(id=topic_id)
check_topic_owner(topic.owner, request)
if request.method != "POST":
form = EntryForm()
else:
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return redirect('composetopic:topic', topic_id=topic_id)
context = {'topic': topic, 'form': form}
return render(request, 'composetopic/new_entry.html', context)
#login_required()
def edit_entry(request, entry_id):
entry = Entry.objects.get(id=entry_id)
topic = entry.topic
check_topic_owner(topic.owner, request)
if request.method != "POST":
form = EntryForm(instance=entry)
else:
form = EntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return redirect('composetopic:topic', topic_id=topic.id)
context = {'entry': entry, 'topic': topic, 'form': form}
return render(request, 'composetopic/edit_entry.html', context)
def check_topic_owner(owner, request):
if owner != request.user:
raise Http404
i was tried to add delete_entry() function, but its not working
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from .models import Topic, Entry
from .forms import TopicForm, EntryForm
from django.http import Http404
#login_required()
def delete_entry(request, topic_id):
try:
topic = Topic.objects.get(id=topic_id)
topic.delete()
except Topic.DoesNotExist as e:
topic = None
if request.method != "POST":
form = EntryForm()
else:
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic # <Topic: Topic object (None)> topic or None
return redirect('composetopic:topic', topic_id=topic_id) # deleted id
context = {'topic': topic, 'form': form} # <Topic: Topic object (None)> topic or None
return render(request, 'composetopic/deleted_entry.html', context)
topic should return a null queryset instance if has beed deleted correctly (id exist and object is validated) if is not is just set to None
from app.models import Topic
>>> Topic.objects.all()
<QuerySet [. . .]>
#...
>>> topic = Topic.objects.get(id = id)
>>> topic.delete()
<Topic: Topic object (None)> # You can still access to his attributes but this not exist in database
if id object is not valid, raise an error self.model.DoesNotExists then a try-except statement is used when you call get method, also when you check for type of request ("POST", "GET") you need to modify how and where redirect deleted objects or something like render a page with text 'deleted topic' and some information about deleted object.
I am trying to submit a form, save to database and then show the cleaned_data on a new url. In the form I have a field called job_number. I would like to send the cleaned_data over to 127.0.0.1:8000/quotes/job_number
quote/views.py:
#login_required
def quote_view(request):
data_form = QuoteInformationForm()
if request.method == "POST":
data_form = QuoteInformationForm(request.POST)
if data_form.is_valid():
data_form.save(commit=True)
return redirect('quote')
else:
print('Error')
return render(request, "index.html", {'data_form': data_form})
#login_required
def submitted_quote(request):
return render(request, "quote.html")
urls.py:
urlpatterns = [
path('home/', quote_view, name='home'),
path('quote/', submitted_quote, name='quote'),
Currently all this does is open the form at http://127.0.0.1:8000/home/ using index.html. When I submit it will send the info to the database and redirect me to http://127.0.0.1:8000/quotes. This is fine. Now I just need to show the cleaned data on this url and change the url to include the job_number at the end. How can I do this?
you need to change url pattern like this:
urlpatterns = [
path('home/', quote_view, name='home'),
path('quote/<int:job_number>/', submitted_quote, name='quote'),
]
and submitted_quotes():
#login_required
def submitted_quote(request,job_number):
job = # Get Your job
return render(request, "quote.html", {"job":job})
and :
#login_required
def quote_view(request):
data_form = QuoteInformationForm()
if request.method == "POST":
data_form = QuoteInformationForm(request.POST)
if data_form.is_valid():
data_form.save(commit=True)
return redirect('quote{}'.format(data_form.id))
else:
print('Error')
return render(request, "index.html", {'data_form': data_form})
I am getting stuck with the new_entry functionality , This is a project in the crash course for python book , I have tried to verify my code a number of times not able to figure out why I get this error:
TypeError: cannot convert dictionary update sequence element #0 to a sequence
File views.py
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django.urls import reverse
from .models import Topic
from .forms import TopicForm
from .forms import EntryForm
def index(request):
return render(request, 'learning_logs/index.html')
def topics(request):
# display all the topics
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
# display entries pertaining to a topic
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
def new_topic(request):
# fill a new topic
if request.method != 'POST':
# No data submitted , create a blank form
form = TopicForm()
else:
# post data submitted , process data
form = TopicForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('learning_logs:topics'))
context = {'form': form}
return render(request, 'learning_logs/new_topic.html', context)
def new_entry(request, topic_id):
# Enter a new entry into any topic
topic = Topic.objects.get(id=topic_id)
if request.method != 'POST':
# NO data submitted , create a blank form
form = EntryForm()
else:
# post data submitted , process data
form = EntryForm(data=request.POST)
if form.is_valid():
new_entry = form.save(commit=False)
new_entry.topic = topic
new_entry.save()
return HttpResponseRedirect(reverse('learning_logs:topic',
args=[topic_id]))
context = {'topic': topic, 'form': form}
return render(request, 'learning_logs/new_entry.html', context)
File urls.py
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [ # Homepage
path('', views.index, name='index'),
# topics
path('topics', views.topics, name='topics'),
# entries about each topic
path('<topic_id>/topic/', views.topic, name='topic'),
# page for adding a new topic
path('new_topic/', views.new_topic, name='new_topic'),
# adding entries
path('new_entry/<int:topic_id>/', views.new_entry, name='new_entry'),
]
And this is the place where i am getting stuck with the new_entry functionality, file new_entry.html
{% extends "learning_logs/base.html" %}
{% block content %}
<p>{{ topic }}</p>
<p>New entry</p>
<form action="{% url 'learning_logs:new_entry' topic.id %}" method="post">
{% csrf_token %}
{{form.as_p}}
<button name="submit">add entry</button>
</form>
{% endblock content %}
File forms.py
from django import forms
from .models import Topic
from .models import Entry
class TopicForm(forms.ModelForm):
class Meta:
model = Topic
fields = ['text']
labels = {'text': ''}
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['text']
labels = {'text': ''}
widgets = {'text': forms.Textarea(attrs={'cols', 80})}
File models.py
from django.db import models
class Topic(models.Model):
# A topic that the user is learning about
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
# Return the info as a string
return self.text
class Entry(models.Model):
# something specific learned about a topic
topic = models.ForeignKey(Topic, on_delete=models.PROTECT)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = "entries"
def __str__(self):
# A representation of the data in string form
if len(self.text) > 50:
return str(self.text)[:50] + "...."``
else:
return self.text
I suspect the error is in your EntryForm.Meta.widgets; attrs is supposed to be a dict (should be written as {'cols': 80}), but you are passing a set with 2 elements (you wrote {'cols', 80}).
Notice how you used a comma , instead of a colon :.
The corrected code would be:
class EntryForm(forms.ModelForm):
class Meta:
...
widgets = {
'text': forms.Textarea(attrs={'cols': 80}),
}
I have a Django Form (ModelForm) which has a number of fields. When the user presses submit these are then saved to a database. What I am struggling to work out is, how do I then output/render these results in some other HTML page.
Models.py
from django.db import models
# Create your models here.
class Contract(models.Model):
name = models.CharField(max_length=200)
doorNo = models.SlugField(max_length=200)
Address = models.TextField(blank=True)
Forms.py
from django import forms
from contracts.models import Contract
class GenerateContract(forms.ModelForm):
class Meta():
model = Contract
fields = '__all__'
Views.py
from django.shortcuts import render
from contracts.forms import GenerateContract
# Create your views here.
def index(request):
return render(request, 'contracts/index.html')
def contractview(request):
form = GenerateContract()
if request.method == "POST":
form = GenerateContract(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print('ERROR')
return render(request,'contracts/contracts.html',{'form':form})
At the moment, I am returning the 'Index' Home page of the app as a placeholder.
After validation, the form data is found in form.cleaned_data dictionary. So you can pass that back to the template and display it as you see fit.
from django.shortcuts import render
from contracts.forms import GenerateContract
# Create your views here.
def index(request):
return render(request, 'contracts/index.html')
def contractview(request):
form = GenerateContract()
if request.method == "POST":
form = GenerateContract(request.POST)
if form.is_valid():
form.save(commit=True)
return render(request,'contracts/contracts.html',{'form_data': form.cleaned_data})
else:
print('ERROR')
return render(request,'contracts/contracts.html',{'form':form})
If you want to show the form with the saved values, you can render the template with form and fill the instance input . like this:
from django.shortcuts import render
from contracts.forms import GenerateContract
# Create your views here.
def index(request):
return render(request, 'contracts/index.html')
def contractview(request):
form = GenerateContract()
if request.method == "POST":
form = GenerateContract(request.POST)
if form.is_valid():
saved_instance = form.save(commit=True)
return render(request,'contracts/contracts.html',{'form':GenerateContract(instance=saved_instance)})
else:
print('ERROR')
return render(request,'contracts/contracts.html',{'form':form})
I have a contact page for my website. The contact form is working but I want to add a background image to this page (which is saved in the database).But how can I combine my email(request) and a query to get the image ?
views.py
from django.core.mail import send_mail, BadHeaderError
from django.http import HttpResponse
from django.shortcuts import render, redirect
from .forms import ContactForm
def email(request):
if request.method == 'GET':
form = ContactForm()
else:
form = ContactForm(request.POST)
if form.is_valid():
subject = form.cleaned_data['subject']
from_email = form.cleaned_data['from_email']
message = form.cleaned_data['message']
try:
send_mail(subject, message, from_email, ['admin#example.com'])
except BadHeaderError:
return HttpResponse('Invalid header found.')
return redirect('./success')
return render(request, "contact/email.html", {'form': form})
def success(request):
return HttpResponse('Success! Thank you for your message.')
models.py
from django.db import models
class Background(models.Model):
name = models.CharField(max_length=200, null=False)
image = models.ImageField(upload_to='./background/', default=None)
def __str__(self):
return self.name
urls.py
from django.conf.urls import url
from . import views
app_name = 'contact'
urlpatterns = [
url(r'^$', views.email, name='email'),
url(r'^success/$', views.success, name='success'),
]
You could retrieve the image on your view:
def get_background():
try:
background = Background.objects.get(name="your image name") # add your filters where to get the image
if background.image and background.image.url:
return background.image.url
except Background.DoesNotExist:
return
def email(request):
if request.method == 'GET':
form = ContactForm()
else:
...
...
# add the background to context
return render(request, "contact/email.html", {'form': form, 'background_img': get_background()})
And in your HTML (it's just a exemple, always try to style your site with a separate css file):
...
<div {% if background_img %}style="background: url('{{background_img}}') center no-repeat;"{% endif %}>
...