Tango with Django Chapter 6 - URL won't work - python

I have been going through "Tango with Django" and have been unable to solve this problem myself or by looking online. Would anyone know how to approach it?
The relevant page should be opened when I click on the link, but none are going through, which makes me assume something in my view.py file is wrong or even in my url.py or model.py file (index.html seems to be working correctly).
Views.py
# Create your views here.
from django.http import HttpResponse
from django.shortcuts import render
from Spaces.models import Category, Page
def index(request):
# Query the databse for a list of ALL categories currently stored.
# Order the categories by no likes in descending order .
# Retrieve the top 5 only - or all if less than 5.
# Place the list in context_dict dictionary
# that will be passed to the template engine.
category_list = Category.objects.order_by('-likes')[:5]
context_dict = {'categories': category_list}
# Render the response and send it back!
return render(request, 'Spaces/index.html', context=context_dict)
def about(request):
context_dict = {'boldmessage':"Crunchy, creamy, cookie, candy, cupcake!"}
return render(request, 'Spaces/about.html', context=context_dict)
def show_category(request, category_name_slug):
# Create a context dictionary which we can pass
# to the template rendering engine.
context_dict = {}
try:
# Can we find a category name slug with the given name?
# If we can't, the .get() method raises a DoesNotExist exception.
# So the .get() method returns one model instance or raises an exception.
category = Category.objects.get(slug=category_name_slug)
# Retrieve all of the associated pages.
# Note that filter() will return a list of page objects or an empty list
pages = Page.objects.filter(category=category)
# Adds our results list to the template context under name pages.
context_dict['pages'] = pages
# We also add the category object from
# the database to the context dictionary.
# We'll use this in the template to verify that the category exists.
context_dict['category'] = category
except Category.DoesNotExist:
# We get here if we didn't find the specified category.
# Don't do anything -
# the template will display the "no category" message for us.
context_dict['category'] = None
context_dict['pages'] = None
# Go render the response and return it to the client.
return render(request, 'Spaces/category.html', context_dict)
Urls.py
from django.conf.urls import url
from Spaces import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^about/$', views.about, name='about'),
url(r'^category/(?P<category_name_slug>[\w\-]+)/$',
views.show_category, name='show_category'),
]
models.py
from django.db import models
from django.template.defaultfilters import slugify
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
slug = models.SlugField(unique=True, blank=True, null=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
class Meta:
verbose_name_plural = 'categories'
def __str__(self):
return self.name
class Page(models.Model):
category = models.ForeignKey(Category, on_delete=models.PROTECT)
title = models.CharField(max_length=128)
url = models.URLField()
views = models.IntegerField(default=0)
def __str__(self): # For Python 2, use __unicode__ too
return self.title
index.html
<!DOCTYPE html>
{% load staticfiles %}
<html>
<head>
<title>Spaces</title>
</head>
<body>
<h1>Spaces says...</h1>
<div>hey there partner!</div>
<div>
{% if categories %}
<ul>
{% for category in categories %}
<li>
{{ category.name }}
</li>
{% endfor %}
</ul>
{% else %}
<strong>There are no categories present.</strong>
{% endif %}
</div>
<div>
About Space<br />
<img src="{% static 'images/Spaces.jpg' %}" alt="Picture of Rango" />
</div>
</body>
</html>
populate_spaces.py (test script)
import os
os.environ.setdefault('DJANGO_SETTINGS_MODULE',
'Space.settings')
import django
django.setup()
from Spaces.models import Category, Page
def populate():
#First, we will create lists of dictionaries containing the pages
# we want to add into each category.
# Then we will create a dictionary of dictionaries for our categories.
# This might seem a little bit confusing, but it allows us to iterate
# through each data structure, and add the data to our models.
python_pages = [
{"title": "Prahran",
"url":"http://docs.python.org/2/tutorial/", "views":20},
{"title": "South Yarra",
"url":"http://docs.python.org/2/tutorial/", "views":25},
{"title": "etcetera",
"url":"http://docs.python.org/2/tutorial/", "views":35}
]
django_pages = [
{"title" : "Official Django Tutorial",
"url" :"https://docs.djangoproject.com/en/1.9/intro/tutorial01/", "views":36},
{"title":"Django Rocks",
"url":"http://www.djangorocks.com/", "views":23},
{"title":"How to Tango with Django",
"url":"http://www.tangowithdjango.com/", "views":45}
]
other_pages = [
{"title":"Bottle",
"url":"http://bottlepy.org/docs/dev/", "views":3},
{"title":"Flask",
"url":"http://flask.pocoo.org",
"views":34}]
cats = {"Python": {"pages": python_pages, "views": 128, "likes":64},
"Django": {"pages": django_pages, "views": 64, "likes":32},
"Other Frameworks": {"pages": other_pages, "views": 32, "likes":16} }
# If you want to add more categories or pages,
# Add them to the dictionaries above.
# The code below goes through the cats dictionary, then adds each category
# and then adds all the associated pages for that category.
for cat, cat_data in cats.items():
c = add_cat(cat,cat_data)
for p in cat_data["pages"]:
add_page(c, p["title"], p["url"], p["views"])
#Print out the categories we have added.
for c in Category.objects.all():
for p in Page.objects.filter(category=c):
print("-{0})-{1}".format(str(c), str(p)))
def add_page(cat, title, url, views=0):
p = Page.objects.get_or_create(category=cat, title=title)[0]
p.url=url
p.views=views
p.save()
return p
def add_cat(name, cat_data):
c = Category.objects.get_or_create(name=name)[0]
c.likes = cat_data["likes"]
c.views = cat_data["views"]
c.save()
return c
# Start execution here!
if __name__ == '__main__':
print("Starting Spaces population script...")
populate()

I fixed the issue.
Essentially I had indented a return function in my view.py file incorrectly!

Related

Displaying model data in Django using for loop

Im attempting to display all the course Names in my Course Model,
{% for course in object_list%}
<li>{{ course.courseName}} </li>
{% endfor %}
</ol>
Heres a snippet from the HTML page Im trying to display them in.
here's my models.py
# Create your models here.
SEMESTER_CHOICES = (
("SPR 21", "SPR 21"),
("FA 21", "FA 21"),
("SPR 22", "SPR 22"),
("FA 22", "FA 22"),
("SPR 23", "SPR 23"),
("FA 23", "FA 23"),
("SPR 24 ", "SPR 24"),
("FA 24", "FA 24"),
)
PROGRAM_CHOICES = (
("Architectural Science","Architectural Science"),
("Civil Engineering","Civil Engineering"),
("Computer Information Technology","Computer Information Technology"),
("Computer Science","Computer Science"),
("Construction Management","Construction Management"),
("Electrical Engineering","Electrical Engineering"),
("Engineering Technology Management","Engineering Technology Management"),
("Manufacturing Engineering","Manufacturing Engineering"),
("Mechanical_Engineering","Mechanical_Engineering")
)
# declaring a Student Model
class AddCourse(models.Model):
program = models.CharField(
max_length = 20,
choices = PROGRAM_CHOICES,
default = 'Architecural Science'
)
courseName = models.CharField(max_length=250)
semester = models.CharField(
max_length = 20,
choices = SEMESTER_CHOICES,
default = 'SPR 21'
)
preRequisites = models.TextField()
def __str__ (self):
return self.courseName
and here is my views.py
from django.shortcuts import render
from .models import AddCourse
from django.contrib.auth.decorators import login_required
# Create your views here.
#login_required
def currentpathway(request):
return render(request, "SEAS_Course_Planner/home.html")
#login_required
def newpathway(request):
return render(request, "SEAS_Course_Planner/newpathway.html")
Nothing is printing out and I believe the object list is empty, but I've added courses via the admin page, any suggestions. Im new to Django as you can tell hahaha.
I think you need to go through the docs from beginning.
Here in your view first you need to return the queryset from your Course model and need to pass the queryset as a context in your template.
def currentpathway(request):
courses_list = AddCourse.objects.all()
context = {'courses_list':courses_list}
return render(request, "SEAS_Course_Planner/home.html", context)
Now in the template:
{% for course in courses_list %}
<li>{{ course.courseName}} </li>
{% endfor %}

Search field in Django is not redirecting to detail view

I am adding a search field in my blog so people can put the name of the blog they want to read and a list of items come up and after clicking on any of the list items, it will redirect to the detail view. However, in my case,If I put anything in search, it is not redirecting to a detail view but going to http://127.0.0.1:8000/home/search/2 instead of http://127.0.0.1:8000/home/list/2/.
I have posted my models, views, URLs and template files below.
Is there any reverse method I need to use here to redirect and what changes I need in my template file?
models.py
from django.db import models
class Category(models.Model):
cat_name = models.CharField(max_length = 256, blank = True)
def __str__(self):
return self.cat_name
class Blog(models.Model):
name = models.CharField(max_length = 256, blank = True)
pub_date = models.DateTimeField('date published')
text = models.TextField(blank = True)
category = models.ForeignKey(Category, on_delete=models.CASCADE,
related_name='categories', verbose_name = 'blog_categories')
def __str__(self):
return self.name
views.py
from django.shortcuts import render
from homepage.models import Blog
from django.views.generic import TemplateView, ListView, DetailView
from homepage import models
from django.db.models import Q
class Home(TemplateView):
template_name = 'homepage/index.html'
class BlogListView(ListView):
context_object_name = 'blogs'
model = models.Blog
template_name = 'homepage/blog_list.html'
class BlogDetailView(DetailView):
context_object_name = 'blogs_detail'
model = models.Blog
template_name = 'homepage/blog_detail.html'
def get_queryset(self):
query = self.request.GET.get('q')
return Blog.objects.filter(
Q(name__icontains = query) | Q(name__icontains = query) )
class SearchResultsListView(ListView):
model = Blog
context_object_name = 'blog_list'
template_name = 'homepage/search_result_list.html'
def get_queryset(self):
query = self.request.GET.get('q')
return Blog.objects.filter(
Q(name__icontains = query) | Q(name__icontains = query) )
urls.py
from django.urls import path
from homepage import views
from homepage.views import SearchResultsListView
app_name = 'homepage'
urlpatterns = [
path('', views.Home.as_view(), name = 'index'),
path('list/', views.BlogListView.as_view(), name = 'blog-list'),
path('list/<int:pk>/', views.BlogDetailView.as_view(), name = 'blog-list'),
path('search/', SearchResultsListView.as_view(), name = 'search_result'),
]
index.html
<div class="grid-item-1">
<h1>G</h1>
<input type="button" name="" value="Back to Home" placeholder="">
<form action="{% url 'home:search_result' %}" method = 'get'>
<input type="text" name="q" placeholder="search">
</form>
</div>
search_result_list.html
{% for blog in blog_list %}
<ul>
<li>
{{blog.name}}
{{blog.address}}
here
</li>
</ul>
{% endfor %}
the URL redirects to http://127.0.0.1:8000/home/search/2 and its a 404 page.
how can I redirect it to the detail view page http://127.0.0.1:8000/home/list/1/ and see the detail of the list pulled by search result.
The reason this happens is because {{ blog.id }} just contains a number, for example 2. It will be appended to the URL. You can fix this by prepending the URL with a slash (/) and write list with:
{{blog.name}}
{{blog.address}}
here
But it is likely better to make use of the {% url … %} template tag [Django-doc]:
{{blog.name}}
{{blog.address}}
here
In your BlogDetailView, there is no q parameter, so you can remove the get_queryset:
class BlogDetailView(DetailView):
context_object_name = 'blogs_detail'
model = models.Blog
template_name = 'homepage/blog_detail.html'
# no get_queryset
Furthermore perhaps you should consider renaming the DetailView from blog-list, to blog-detail, or something similar.

How do i import single object from django to a html template

i am creating a website with django and i have 2 models in it,1:Gifi(contains .gif images) and 2:categorite! When i click one of the .gif images i want to be sent to another html template where that image shows and information about it.I have done some coding and when i click the image i get to the html page but the problem is that no data from django gets imported to that html page,except the id on the url.I know the problem is so simple but i am new to this and i dont know the code.
This is the models:
from django.db import models
class categorite(models.Model):
name = models.CharField(max_length=100)
id = models.AutoField(primary_key=True)
class Gifi(models.Model):
foto = models.ImageField(upload_to='website/static/')
emri = models.CharField(max_length=100)
Source = models.CharField(max_length=100)
Kodet = models.CharField(max_length=12)
categoryId = models.ForeignKey(categorite, on_delete=models.CASCADE)
id = models.AutoField(primary_key=True)
This is views.py:
from django.shortcuts import render,get_object_or_404
from .models import Gifi,categorite
# Create your views here.
def home(request):
return render(request, 'website/home.html')
def categories(request):
content = {
'view': categorite.objects.all()
}
return render(request, 'website/categories.html',content)
def PostaCode(request):
return render(request, 'website/PostaCode.html')
def Contact(request):
return render(request, 'website/Contact.html')
def category(request,id):
content = {
'view': Gifi.objects.filter(categoryId_id=id),
}
return render(request, 'website/category.html',content)
def code(request,id):
content = {
'view': get_object_or_404(Gifi,pk=id)
}
return render(request, 'website/code.html',content)
This is the template where i click the image:
{% for gifi in view %}
<a href="{% url 'code' gifi.id %}" class="gif">
<img src="/static/{{gifi.foto}}" id="foto" alt="" >
<p id="source">Source: {{gifi.Source}}</p>
<p id="permbatjaa">Coding: {{gifi.Kodet}}</p>
</a>
{% endfor %}
This is the template where i need to get to, and where information about image should be(code.html):
<img src="/static/{{gifi.foto}}" id="foto" alt="" >
<p>{{gifi.emri}}</p>
It is passed a few days so you might get an answer already but Django's view function and the template are so simple, and you could write:
The models.py:
from django.db import models
class Categorite(models.Model):
# id field is already exists models.Model not necessary
#id = models.AutoField(primary_key=True)
name = models.CharField(max_length=100)
class Gifi(models.Model):
foto = models.ImageField(upload_to='website/static/')
emri = models.CharField(max_length=100)
source = models.CharField(max_length=100)
kodet = models.CharField(max_length=12)
category = models.ForeignKey(Categorite, on_delete=models.CASCADE, related_name='gifis')
And the views.py:
def category(request, id):
# For the foreign key fields _id field auto created also
objs = Gifi.objects.filter(category_id=id)
return render(request, 'website/category.html', {'gifis': objs})
def code(request, id):
obj = get_object_or_404(Gifi, pk=id)
return render(request, 'website/code.html', {'gifi': obj})
And the template website/category.html
{% for gifi in gifis %}
<a href="{% url 'code' gifi.id %}" class="gif">
<img src="/static/{{gifi.foto}}" id="foto" alt="" >
<p id="source">Source: {{gifi.source}}</p>
<p id="permbatjaa">Coding: {{gifi.kodet}}</p>
</a>
{% endfor %}
And the template website/code.html
<img src="/static/{{gifi.foto}}" id="foto" alt="" >
<p>{{gifi.emri}}</p>
To meet PEP 8 spec, under_score recommended for the variable names, and CamelCase is recommended for the class names.
If you are new to Django, I highly recommend the official tutorial page would help a lot.

Django 2.1 passing a variable to template,

Have a question here about passing a variable into a Django template. The goal is to filter a set of photos based off the type of photography. I initially wanted to do it from S3 and the folder that it was in, but that's a little beyond my skill at the moment. I went with just creating different url's that account for that. The issue I'm having is that I'd like to pass the variable into the template that extends the base_layout.html, but it won't render anything for that variable. Am I just miss-understanding how to do it?
Model.py
from django.db import models
# Create your models here.
class Gallery(models.Model):
title = models.CharField(max_length = 50)
body = models.TextField(max_length = 500)
created = models.DateTimeField(auto_now_add = True)
thumb = models.ImageField(default = 'default.jpg', blank = True)
slug = models.SlugField(blank = True)
order = models.CharField(max_length = 2, blank = True)
def __str__(self):
return self.title
def body_preview(self):
return self.body[:50]
class photoUrl(models.Model):
url = models.CharField(max_length = 128)
uploaded_on = models.DateTimeField(auto_now_add = True)
class Photos(models.Model):
title = models.CharField(max_length = 50)
picture = models.ImageField(blank = True)
created = models.DateTimeField(auto_now_add = True)
catagory = models.CharField(max_length=256, choices=[('wedding', 'wedding'), ('portrait', 'portrait'), ('landscape', 'landscape'), ('boudoir', 'boudoir'),], blank = True)
def __str__(self):
return self.title
views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.urls import reverse
from . models import Photos
# Create your views here.
def photo_wedding(request):
photo_list = Photos.objects.filter(catagory = 'wedding').order_by('created')
photoCat = 'Wedding'
return render(request, 'gallery/gallery.html', {'photo_list' : photo_list}, {'photoCat' : photoCat})
urls.py
from django.contrib import admin
from django.urls import path
from . import views
app_name='gallery'
urlpatterns = [
path('wedding/', views.photo_wedding, name='wedding'),
path('portrait/', views.photo_portrait, name='portrait'),
path('landscape/', views.photo_landscape, name='landscape'),
path('boudoir/', views.photo_boudoir, name='boudoir'),
]
gallery.html
{% extends 'gallery/base_layout.html' %}
{% load static %}
{% block gallery %}
<div class="gallery" id="gallery">
<div class="container">
<div class="w3l-heading">
<h3>{{photoCat}}</h3>
<div class="w3ls-border"> </div>
</div>
</div>
{% endblock gallery %}
From the definition of render:
render(request, template_name, context=None, content_type=None, status=None, using=None)
Combines a given template with a given context dictionary and returns an HttpResponse object with that rendered text.
the render method takes the first parameter as a request, the second parameter as template_name and the third parameter is a context which is of type dictionary you choose to pass to the template, you can access all the values of dictionary with the key.
So your method should look like below:
def photo_wedding(request):
photo_list = Photos.objects.filter(catagory = 'wedding').order_by('created')
photoCat = 'Wedding'
return render(request, 'gallery/gallery.html', {'photo_list' : photo_list, 'photoCat' : photoCat})
Why are you passing two dictionaries. Just add a key. That is the context data.
In class based views you can also overload the method get_context_data
With the render() function, the third argument is the context. The context is a dictionary used to send variable to templates. No need to pass two dicts
def photo_wedding(request):
photo_list = Photos.objects.filter(catagory = 'wedding').order_by('created')
photoCat = 'Wedding'
context = {'photo_list' : photo_list,'photoCat' : photoCat}
return render(request, 'gallery/gallery.html', context)

Django - multiple url params with included template

I'm trying to include an template and utilize it in two different views, where the first one gets one url param and the second gets the same plus another one. Inside my included template there is an iteration with a {% url %} tag that I need to pass both params, since the second view needs them, but doing this causes NoReverseMatch when trying to render my first view, probably because it only accepts one param. Is there any way to specifying the second param is optional?
Here is my code:
urls.py
...
url(r'^portfolio/(?P<category_slug>[-\w]+)/$', views.category, name='category'),
url(r'^portfolio/(?P<category_slug>[-\w]+)/(?P<album_slug>[-\w]+)/$', views.album, name='album'),
...
models.py
...
class Album(models.Model):
cover = models.ImageField()
title = models.CharField(max_length=200, unique=True)
description = models.CharField(max_length=200, blank=True)
posts = models.ManyToManyField(Post, blank=True)
slug = models.SlugField(max_length=200)
class Category(models.Model):
cover = models.ImageField()
title = models.CharField(max_length=200, unique=True)
albums = models.ManyToManyField(Album, blank=True)
slug = models.SlugField(max_length=200)
#receiver(pre_save, sender=Album)
#receiver(pre_save, sender=Category)
def save_slug(sender, instance, *args, **kwargs):
instance.slug = slugify(instance.title)
...
views.py
...
def index(request):
main_categories = Category.objects.filter(...)
return render(request, 'index.html', {'main_categories': main_categories})
def category(request, category_slug):
try:
category_selected = Category.objects.get(slug=category_slug)
except Category.DoesNotExist:
category_selected = None
albums = category_selected.albums.all()
return render(request, 'category.html', {
'category_selected': category_selected,
'albums': albums
})
def album(request, category_slug, album_slug):
try:
category_selected = Category.objects.get(slug=category_slug)
album_selected = Album.objects.get(slug=album_slug)
except Category.DoesNotExist:
category_selected = None
except Album.DoesNotExist:
album_selected = None
posts = album_selected.posts.all()
return render(request, 'album.html', {
'category_selected': category_selected,
'album_selected': album_selected,
'posts': posts
})
...
includedtemplate.html
...
{% for obj in objects %}
...
<a href="{% url view category_slug=obj.slug album_slug=obj.slug %}">
...
{% endfor %}
...
index.html
...
{% include 'includedtemplate.html' with objects=main_categories view='category' %}
...
EDIT:
I've managed to solve this by separating my urls with only one different slug each. This is simpler and fits my situation better, considering that I had a M2M for Category and Album and this could cause many urls for a single album.
You can combine views and set None for album_slug.
def combined_view(request, category_slug, album_slug=None):
category_selected = Category.objects.filter(slug=category_slug).first()
album_selected = None
posts = None
template = 'category.html'
if album_slug:
album_selected = Album.objects.filter(slug=album_slug).first()
posts = album_selected.posts.all()
template = 'album.html'
return render(request, template, {
'category_selected': category_selected,
'album_selected': album_selected,
'posts': posts
})
Also the order of urls is important - first url should be with one parameter, second with two parameters:
url(r'^portfolio/(?P<category_slug>[-\w]+)/$', views.combined_view, name='cview'),
url(r'^portfolio/(?P<category_slug>[-\w]+)/(?P<album_slug>[-\w]+)/$', views.combined_view, name='cview'),
P.S. Instead of try..except in views you can use filter().first(). It is faster to write.

Categories