learning-website-django1-6 Exercise 2: Breadcrumbs, Previous/Next and Folder Listings =========================================================================================== Add the third layer (where you are on this site) and the pages that make the breadcrumb links go somewhere: a listing for every folder. The page view now serves either a page or a folder, and a folder listing needs two new templates and the helpers already in menus.py (breadcrumbs, neighbours, listing). Save the view and templates: Save as apps/content/views.py: from django.http import Http404 from django.shortcuts import render from apps.navigation.labels import humanize from apps.navigation.menus import breadcrumbs, listing, neighbours from .models import Page def home(request, site): pages = Page.objects.light().filter(site=site) return render(request, "theme/home.html", { "page_count": pages.count(), "course_count": pages.exclude(course=None).values("course").distinct().count(), "breadcrumbs": [], }) def page(request, site, page_path): """An address is either a page (/hungary/x/chapter/ is the file hungary/x/chapter.html) or a folder (/hungary/x/ lists what is inside it). Anything else is a 404.""" bare = page_path.strip("/") found = Page.objects.filter(path=bare + ".html", site=site).first() # a page of ANOTHER site is a 404 too if found is not None: before, after = neighbours(found) folder = found.path.rsplit("/", 1)[0] if "/" in found.path else "" return render(request, "theme/page.html", { "page": found, # a page with its own
chapter {n}
") for n in numbers] def make_page(path, site, title="A page", **kw): return Page.objects.create(path=path, site=site, kind="lesson", title=title, fragment="x
", **kw) class LabelTests(SimpleTestCase): def test_small_words_are_lower_case_and_acronyms_upper_case(self): self.assertEqual(humanize("data-science-and-ml"), "Data Science and ML") self.assertEqual(humanize("cloud-and-devops"), "Cloud and DevOps") def test_product_names_use_their_own_capitals(self): self.assertEqual(humanize("javascript"), "JavaScript") self.assertEqual(humanize("ios-development"), "iOS Development") self.assertEqual(humanize("maths-for-programmers"), "Maths for Programmers") def test_the_first_word_is_always_capitalised(self): self.assertEqual(humanize("the-history-of-football"), "The History of Football") def test_an_extension_is_dropped(self): self.assertEqual(humanize("vim_lesson.html"), "Vim Lesson") class GlobalBarTests(SimpleTestCase): @override_settings(SITE_URL_TEMPLATE=TEMPLATE) def test_every_site_appears_once_and_exactly_one_is_current(self): bar = global_bar("systems") self.assertEqual([s["name"] for s in bar], list(SITES)) self.assertEqual([s["name"] for s in bar if s["current"]], ["systems"]) self.assertEqual(bar[0]["url"], "https://languages.osztromok.com/") @override_settings(SITE_URL_TEMPLATE="http://{site}.localhost:8000") def test_development_addresses_use_localhost_and_the_port(self): self.assertEqual(global_bar("ai")[0]["url"], "http://languages.localhost:8000/") class SiteMenuTests(TestCase): def setUp(self): cache.clear() def test_only_folders_that_have_pages_appear_in_the_site_maps_order(self): make_page("japan/x/a.html", "languages") make_page("france/y/b.html", "languages") self.assertEqual([i["label"] for i in site_menu("languages")], ["France", "Japan"]) def test_the_menu_for_one_site_never_shows_another_sites_folders(self): make_page("linux/x/a.html", "systems") self.assertEqual(site_menu("languages"), []) def test_a_sidebar_entry_only_appears_where_sidebar_pages_exist_for_that_site(self): make_page("sidebar/linux/cheat.html", "systems") self.assertEqual(site_menu("systems")[-1]["label"], "Sidebar") self.assertEqual(site_menu("languages"), []) def test_the_menu_is_cached_and_an_import_clears_it(self): make_page("japan/x/a.html", "languages") site_menu("languages") with self.assertNumQueries(0): site_menu("languages") make_page("france/y/b.html", "languages") self.assertEqual(len(site_menu("languages")), 1) # still the cached copy invalidate_menus() self.assertEqual(len(site_menu("languages")), 2) class BreadcrumbTests(TestCase): def test_a_course_folder_is_labelled_with_the_courses_real_name(self): make_course() trail = breadcrumbs("languages", "hungary/hungarian-basic-3", last_label="Chapter 1") self.assertEqual([c["label"] for c in trail], ["Languages", "Hungary", "Hungarian Basic Conversation 3", "Chapter 1"]) self.assertEqual([c["href"] for c in trail], ["/", "/hungary/", "/hungary/hungarian-basic-3/", None]) def test_the_folder_you_are_on_is_not_a_link(self): trail = breadcrumbs("languages", "hungary") self.assertEqual([c["href"] for c in trail], ["/", None]) def test_the_site_home_is_just_the_site(self): self.assertEqual(breadcrumbs("languages", ""), [{"label": "Languages", "href": "/"}]) class NeighbourTests(TestCase): def setUp(self): self.pages = make_chapters(make_course()) def test_neighbours_follow_the_chapter_number_not_the_file_name(self): by_no = {p.chapter_no: p for p in self.pages} self.assertEqual([n.chapter_no if n else None for n in neighbours(by_no[3])], [2, 10]) self.assertEqual([n.chapter_no if n else None for n in neighbours(by_no[10])], [3, None]) def test_the_first_chapter_has_no_previous(self): self.assertEqual(neighbours(self.pages[0])[0], None) def test_a_page_with_no_course_or_number_has_no_neighbours(self): p = make_page("hungary/hungarian-lessons/x.html", "languages") self.assertEqual(neighbours(p), (None, None)) class ListingTests(TestCase): def test_a_folder_lists_its_subfolders_and_its_own_pages(self): make_page("hungary/a/one.html", "languages") make_page("hungary/a/two.html", "languages") make_page("hungary/b/three.html", "languages") make_page("hungary/top.html", "languages", title="Top") out = listing("languages", "hungary") self.assertEqual([(f["label"], f["count"]) for f in out["folders"]], [("A", 2), ("B", 1)]) self.assertEqual([p.title for p in out["pages"]], ["Top"]) def test_a_course_folder_lists_its_chapters_in_numeric_order(self): make_chapters(make_course(), numbers=(10, 2, 1)) out = listing("languages", "hungary/hungarian-basic-3") self.assertEqual([p.chapter_no for p in out["pages"]], [1, 2, 10]) self.assertEqual(out["course"].name, "Hungarian Basic Conversation 3") def test_an_empty_or_unknown_folder_is_none(self): self.assertIsNone(listing("languages", "hungary/nothing")) def test_a_site_only_lists_its_own_pages(self): make_page("linux/x/a.html", "systems") self.assertIsNone(listing("languages", "linux")) class NavigationPagesTests(TestCase): def setUp(self): cache.clear() make_chapters(make_course()) self.client = Client(HTTP_HOST="languages.localhost") def test_a_chapter_shows_the_three_layers_and_its_neighbours(self): html = self.client.get("/hungary/hungarian-basic-3/hungarian_basic_conversation_3_2/").content.decode() self.assertIn('aria-label="Sites"', html) # layer 1 self.assertIn('aria-label="Languages subjects"', html) # layer 2 self.assertIn('aria-label="Breadcrumb"', html) # layer 3 self.assertIn('rel="prev"', html) self.assertIn('rel="next"', html) self.assertIn("Hungarian Basic Conversation 3", html) def test_exactly_one_site_is_marked_current_in_the_global_bar(self): html = self.client.get("/").content.decode() bar = html.split('aria-label="Sites"')[1].split("")[0] self.assertEqual(bar.count('aria-current="true"'), 1) def test_the_active_subject_is_marked_in_the_site_menu(self): html = self.client.get("/hungary/hungarian-basic-3/hungarian_basic_conversation_3_1/").content.decode() menu = html.split('aria-label="Languages subjects"')[1].split("")[0] self.assertIn('href="/hungary/" aria-current="true"', menu) def test_a_folder_address_shows_a_listing(self): response = self.client.get("/hungary/hungarian-basic-3/") self.assertEqual(response.status_code, 200) html = response.content.decode() self.assertIn("