Wagtail for Beginners | Kalob Taulien | Skillshare
Search

Playback Speed


1.0x


  • 0.5x
  • 0.75x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 1.75x
  • 2x

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.

      Introduction to Wagtail for Beginners

      1:37

    • 2.

      What is Wagtail CMS?

      2:09

    • 3.

      How to Install Wagtail

      10:49

    • 4.

      Getting Started

      3:26

    • 5.

      Logging into the Admin

      2:17

    • 6.

      Wagtail Walkthrough

      10:28

    • 7.

      Editing the Home Page

      23:20

    • 8.

      Working with the Base Template

      7:06

    • 9.

      Services App and Pages

      20:19

    • 10.

      Service Page

      11:42

    • 11.

      Service Listing Page

      14:19

    • 12.

      Adding Page Validation

      7:36

    • 13.

      Adding a Header and Footer

      7:32

    • 14.

      Adding Debug Tools

      15:47

    • 15.

      Flex/Misc Page

      6:37

    • 16.

      Testimonial Objects

      16:13

    • 17.

      Welcome to StreamFields

      5:52

    • 18.

      Creating the StreamField App

      3:05

    • 19.

      Your First StreamField

      13:48

    • 20.

      Repeating StreamFields

      12:55

    • 21.

      Simplifying Repeating StreamFields

      18:29

    • 22.

      Custom StreamField Logic

      9:12

    • 23.

      Image and Text Block

      12:50

    • 24.

      Radio Block

      4:30

    • 25.

      Call to Action Block

      5:37

    • 26.

      Testimonials and Snippets

      9:51

    • 27.

      Table StreamField

      11:21

    • 28.

      RichText StreamField

      15:27

    • 29.

      Image Block StreamField

      6:18

    • 30.

      Custom StreamField Validation

      9:53

    • 31.

      Limiting Pages

      11:44

    • 32.

      Navigation Menus

      17:47

    • 33.

      Navigation Menu Templates

      16:09

    • 34.

      Contact Forms Part 1

      19:38

    • 35.

      Contact Forms Part 2

      9:57

    • 36.

      Limiting Contact Form Choices

      4:55

    • 37.

      .webp Image Support

      2:49

    • 38.

      Other Ways to Learn Wagtail

      10:47

    • 39.

      Global Site Settings

      14:29

    • 40.

      Changing the Wagtail Admin Logo

      2:38

    • 41.

      Adding Caching

      15:14

    • 42.

      Caching the Navigation and Footer

      12:08

    • 43.

      Adding a Sitemap

      2:37

    • 44.

      Prepping for Launch

      7:08

    • 45.

      Website Launch!

      38:33

  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.

284

Students

--

Projects

About This Class

Welcome to Wagtail for Beginners!

This course is designed for developers who are new to Wagtail CMS. Together, we'll create a brand new Wagtail website from scratch, and at the end we'll deploy it to the web. Here's a preview of the final website: Rocketman

Note: This course assumes you have a basic understanding of Python. If you know some Django, that's great, but not required.

If you're unsure whether this course is for you or not, consider the follow topics we'll be covering together, and if you're unfamiliar or not completely comfortable with all of these subjects then it's likely this course is for you. Here's what we'll be learning:

  • Installation
    • You'll learn how to install Wagtail using venv and Pipenv
  • Create 7 custom
    • Including services, misc pages, testimonials, StreamFields, a custom menu, contact pages, and global site settings
  • Contact pages
    • Creating the initial page and the landing page
    • Customizing contact form options
  • StreamFields
    • Basic StreamFields
    • Repeating StreamFields
    • Custom StreamField validation
    • Table blocks
    • Reusing StreamField components
    • Adding additional logic to StreamFields
    • 2 ways to create StreamFields
  • Custom pages
    • Page validation
    • Custom page fields
    • Working with images and foreign keys to other models
  • Create listing pages to automatically populate content as soon as you create a new page
  • Caching
    • How to speed up your website with caching
    • How to delete caching when you save a page or model
  • Create a custom menu
    • You'll create a sortable menu system from scratch
    • You'll be introduced to Orderables and Clusterable Models
    • Register the menu with Wagtails ModelAdmin
    • Create a custom Django template tag to work with your menu templates
  • Create Django models
    • Use Wagtail as the main admin interface
    • How to turn Django models into reusable pieces of data (Wagtail snippets)
  • Create custom site settings
    • You'll make site settings globally accessible
    • Cache these settings
    • And delete cache when the values are updated
  • Custom image cropping and renditions
    • And .webp image support
  • Creating a sitemap
  • 2 super helpful debugging tools
  • My personal methods for learning Wagtail quickly and efficiently
  • Launch your website on a brand new server
    • Using Digital Ocean and Ubuntu 18 with Nginx and Gunicorn
  • Install an error monitoring service for your live website

And here are some bonuses this course comes with:

  • All the source code
  • The frontend build tools to customize the theme
  • A sample nginx config
  • Sample gunicorn files

If you think you could benefit from a course like this, definitely enroll today and let's get started.

(Preview of the website we'll be creating)

Meet Your Teacher

Teacher Profile Image

Kalob Taulien

Web Development Teacher

Teacher

Hi everybody! I'm Kalob Taulien.

 

Here's the TL;DR (short) version about me:

I have been coding since 1999 and teaching people how to code since 2013 I have over 350,000 web development students world-wide I'm on the Wagtail CMS core development team (Wagtail is Python's #1 most popular website making system) I try my best to answer EVERY question my students have  I love teaching — it's definitely one of my natural talents  Also I love goats! (Great conversation starter with me if we ever get to meet in person)

Below you can find all my Skillshare courses. The categories go from easiest to hardest, except for the Misc. Coding Courses at the very end. 

If you're brand new to coding, start with BEGINNERS WEB DEV.&nb... See full profile

Related Skills

Development Web Development
Level: Beginner

Class Ratings

Expectations Met?
    Exceeded!
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.

Transcripts

1. Introduction to Wagtail for Beginners: Hello and welcome to wag Tail for beginners. My name is Caleb telling I'm going to be your instructor. I'll be the voice behind the video. I'm also a member of the Corps Wag Tail Team. And in this course, we're going to be creating a full wag Tail website from scratch. Now, this course may or may not be for you. We will be covering a lot of different things in this course, but if you're already an advanced wag tail developer, this course probably won't be for you. But here are some of the things that we are going to be learning. In this course, we'll learn how to install Wag Tail with two different virtual environments will create different wag tail pages, will manage templates will add data to template context. So if you don't know what that means, of course is definitely for you. We're going to add and create manage several different stream field types will create a custom jangle model and then allow wag tail to modify it. Use it, enable it reuse the data all over the place. In a thing called a snippet, we'll create listing and detail pages will even create a contact form that can email. Then we'll get into things like global site settings, managing page, hierarchy page and stream field validation. And then I'll tell you some tips and tricks on ways you can learn Wag Tail faster and easier and in a more playful matter will work with images and different image renditions. And then we'll talk about speeding up your website with a very simple form of cashing. Once we're done all that, we're going to deploy this website to the Internet. So if you think you would benefit from a course like this, definitely step inside and we can get started right away. 2. What is Wagtail CMS?: What is wayto rectal is pythons number one most popular content management system. Chances are you've heard of WordPress. Wait Till is like WordPress. If WordPress was professionally developed in this decade, Wait tells extremely developer friendly, and it piggybacks on the Django framework, which is a python framework. And that means wait till is capable of doing everything that jango can. It can also do everything that python can dio. Now, if you're looking to create and launch a website in the next 10 minutes like a WORDPRESS website, this is not the content management system for you. Waiter was built to help people manage their websites properly. That typically means there's ah, continentally person, a designer, a developer, maybe a few other people with some sort of investment in the website. But generally, the three base ingredients for an amazing website is a Web developer, a Web designer and some contents entry person, or someone who deals with a project like a project manager that could even just be your client. The nice thing about Wag Tail is wait till was built with all of this in mind. Now, Wag Tails, a content management system is actually quite performance. It's fast is what I'm saying. It comes with lots of different features that are enabled by default or if they're not enabled by default. They're super easy to enable. But best of all, wake toes 100% open source. And there is a great developer community for any type of support that you need. That being said, if you were considering using, wait till consider this wag tail is fast. It's built off of a very large, secure and proven framework called Django, and all of that is built on top of python. Python runs everything from websites to artificial intelligence to machine learning to simple programs that you might use every day and advanced programs for enterprises that you might not use everyday. You might only use at large, large companies. The python literally does everything, and wag tail is pythons Number one most popular content management system 3. How to Install Wagtail: before we can use wayto wait till needs to be installed, and before that you need to have python installed. So first things first. If you don't already have Python installed, head on over to python dot org's click on Downloads and download the version of Python that you want. I'm going to be using Python 3.7. Any version from Fight on 3.6 or greater is recommended, so choose Python 3.63 point seven or 3.8. Download it, install it. And once you have python running on your computer, you should be able to just open up your command line. Program your favorite command line program and just type Python Dash V, and you will see the version of Python. So I'm using Python 3.7 point two. Once again, you're going to want to use Python 3.6 or newer now to create a new python project. We should put this inside of a virtual environment. Basically, this is a layer between your python project and your computer. We use virtual environments pretty liberally so you can have different package versions in different projects without code conflicting on itself. or you can have one project running Wag Tail 2.4 in another project running. Wait till 2.7 and they will never conflict. Just because you have a different version doesn't mean they should run into problems, and that's what a virtual environment does. It allows us to separate that problem. A virtual environment can actually even go one layer deeper. You can create a virtual environment using a different version of Python. We're going to be using standard virtual environments. So if you're using Python 3.7, your virtual environment is probably going to be python 3.7 as well. But if we wanted to, we could say, 01 project has to use Python 3.4, and another project has used Python 3.7. And maybe we're on another project. And heaven forbid, it has to use Python to which, if you're using python to please upgrade to Python three Python to is definitely deprecate ID. Now let's go over to ways to start a virtual environment for a wag tail site. The 1st 1 is with a thing called a Venn VVe envy, and I believe this comes with newer versions of Python, so you shouldn't have to install anything. This is actually one of the easier ways to install a virtual environment. And the second way is using a method called Pippen. And this one is actually easier. But it does require you to actually have pip and installed. And before we do anything, you're going to want to make sure you have pip installed. Pip is pythons Package manager. Who? You, pip Dash V. You can see I'm using Pip 18.1. This is my location of where Pip is being used. And it's running Pip on python 3.7. So that's good. You should be able to write Run, Pip, Dash V and also get some sort of output like this. All right, we're going to install Wag Tail using ven ve envy. So I already have python running and you can see him running Python 3.7 point two. So I'm gonna do Python Dash M event and then the name of whatever virtual environment I want to call. I'm gonna call this Rocket Man Ven. Actually, what I'm going to do is create a new directory first. So let's cancel that Lets do em que dire Rocket Man CD rocket man And you can see my working directory here. So I'm in users Kale, Italian websites, Rocket Man. And now I'm going to do python dash m ven rocketman vend. The name is not really important. It's just your virtual environment. So I'm gonna call it Rocket Man Ven and we're going to see is going to create a new directory for me here. So let's do an ls Dash l. A. And we can see that there's a full during here called Rocketman venture right in there to get into this thing. All we have to do is type source rocketman, Venn been activated, which, if this is your first time, that's gonna look really weird. But, you know, after a little while you get used to it. And I am now inside of this virtual environment and you can see that because it says rocketman ven in brackets to the left of my user. So now at this point, I can do Pip V. We can see I'm using Pip 18.1. It's coming from my virtual environment. That's the folder here. That's the directory Python 3.7 so that all looks good. Let's go ahead. Install wag Tail now. So, Pip install. Wait, Tell. All right, so wait has been installed. I can now do Pip show Wag Tail. It shows me that I'm using Wag Tail version 2.7 Right there. I can also do pip freeze to see all of my packages that are installed in this virtual environments. I've got beautiful soup. Certify Django Django Tree beard, html five, lib pillow requests six your l'Abbe, wag Tail willow. All sorts of stuff. So that's what is insulting my virtual environment. My computer does not have access to that unless I'm inside of that virtual environment. Now I'm able to say that this project is going to run. Wait till 2.7. And in another virtual environment, I can say, hey, use wayto 2.4. Use an older version of wait til use Wag Tail 1.13 if we wanted to. And these will never conflict with each other, which is really, really nice. So now what we need to do is wait till start my site and that's going to create a website called my site. I don't want it to be called my sight. I'm going to be making a website called Rocket Man. So I'm gonna do wit. They'll start Rocket Man. That's going to create a new folder called Rocket Man. But I don't want it to do that. I wanted to create a new project in New Wag Tail website. In the directory that I'm in. I'm already in a directory called Rocket Man. I've got a virtual of in here and I want to create this new project in this directory. So let's go ahead and do that. You can see that that was very fast. Ls Dash L. A. And we now have docker file Home folder. Managed up high requirements on txt Rocketman folder. That's where settings and static files and templates are going to go. We've got a search app and we've got our virtual environment. So now we can do pip install, Dash are requirements dot txt and all of that looks like it's already been satisfied to some degree, and now it's a runner server. We just type python managed up. I run server 0.0 dot zero Colin 8000 and it should say this you should see this alarming red tax, saying You have 89 un applied migrations and if we were to open up our browser and head on over to local host support 8000 we're going to see this air. That's actually a good thing. That means your website is up and running. So I just cancelled my server there with control. See, I don't just cancel your server so it doesn't run anymore. And next we need to provisional database. Because if we skip back over to where this error is showing up on local host 8000 we're going to see it says no such table wag tail core site. A table is a database term. This is looking for a database that set up in a specific way to get started, and it's not set up that way. So let's go ahead and set that up. The proper waste. Let's do Python managed at Pi my great, and we'll see this is going to apply all these different files and then if we rerun her server with Python managed up, I run server 0000 Colon 8000. Let's go back here and refresh your page. Wallach. We have Wait till installed using V envy or event. And then to get out of your virtue environment, you just have to type the word deactivate. You can see that down here. Sorry, that's at the bottom. But yeah, you just like the word deactivate. And that Rocket man ven seems to go away. Another way to do this is you can have your Web toe website already set up. So you've got Alice Dash L A. Let's just pretend that rocketman Vin does not exist. And we wanted to maybe use pip end. All right, so I just clear off my screen You can see which folder I'm in. I'm in the Rocketman folder at Last Dash L. A. You can see that I've got all my virtual end in here as well, so let's just ignore that. But we've got the entire wait till project in there. So remember, all I did was wag tail start, Rocket Man with the dot And that just installed my project into this current directory. Now, if I wanted to use virtual and I could do Pippen shell or pip engine stalling just into Pippen shell, you can see that is creating a new virtual environment using Python 3.7 point four. It's actually different versions Python than what I'm using, just slightly different and all right, we are inside of it and you can see that it's the Rocket Man virtual environment. We've got something that looks little bit different here before it said Caleb telling and my path to which folder I'm in. If I do P W. D. I'm still in my Rocket Man folder and I think you python Dash V I'm using a different version of python, which is neither here nor there. It just happens to be that Pippen have decided to use the latest version of Python for me. I have different python environments set up, so it did some magic behind the scenes there, but for you it would install the same version of pious on that you were currently using on your machine. And then, once again, once you're in here, you could just type python managed up. I run server 0000 8000. But as you can see, it's going to throw you an air because we're in a different virtual environments. The 1st 1 we created was a venue that's a different virtual environment, technically, and this is a pip end. Another virtual environment does the exact same thing, but it just it isn't aware of the things we've already installed. So let's go ahead and do Pip install Dash. Our requirements dot txt and this is just going to install wayto all over again. All right, that's all installed. And now I can run my server once more. Go back to my browser and I could just refresh because this is just from the V envy and it still works perfectly. So that's how we install. Wait till using a V Envy or Pippen. You don't have to use either of these. You can use virtual environments if you want. It's actually called virtual, and you can use docker vagrant. You can use anything you really want, but these are two fairly common ways, and I thought we would cover them since they're pretty easy to set up. And they don't require anything special, like docker or vagrants or any big downloads. Oh, and one last note. Just to get out of here. It's not quite the same as just typing the activate, at least not on my computer. I might work for you. Just type in the word deactivate doesn't quite do the job for me. So what I have to do is hit control. D. So I'm gonna take that out. Control plus D to cancel that and hit control D, and it just exits for me. So make sure you have a virtual environment set up again. It doesn't matter what kind you used. Make sure you ran your pip. Install our dash requirements a txt. Make sure you've got white telling everything installed. And once you see this egg screen, definitely feel free to move onto the next lesson. But don't move on until you see the happy wag tail egg loading screen. 4. Getting Started: all right. Since we're building a website together, we are going to need this same assets, the same file. So I already have a bunch of the front end stuff already done. A bunch of CSS and JavaScript is already compiled, and I'm going to give that to you. So you don't have to worry about doing all the front end stuff because this is not a front end course. This is about learning. Wakil CMS. This is not about learning Boots, Trapper foundation or Web pack or parcel about Js or anything like that. So you'll be able to download this file called rocketman assets. Does that by apologize if that's a little small there. But we have rocketman assets dot zip and you could just extract that and you're going to get front end stuff. So you get all the front end goodies all the JavaScript, the source and CSS and all that good stuff in there. Package of Jason. If you want to do the front and stuff, you can run NPM install and you'll just install everything. Uh, or you can just totally ignore all of that and we can go into the Rocketman folder and here . You're going to see you CSS images and JavaScript. You want to want to select all of these, Go into your actual rocketman project. So we we've got, like the rockem inventive from the last project even have a pit file because I used a pip environment as well. So let's go into Rocket Man. If your project is not named Rocket Man, it's going to be called whatever your project is. So in this directory here, this is where I installed my wag tail app. This is where I created my wait till website. This might not be called Rocket Man. This might be called your website. This might be called something completely different, but it's going to be the name of your project. Whatever you typed into your command line, then you're going to find a folder called Static. And we should already see CSS and JavaScript in there. Let's go ahead and just paste that in there. This is just saying a folder name CSS already exists in this location. Do I want to merge? Will replace that. I was going to replace all of it because I don't need to merge anything. I just need to overwrite that stuff. Now, on a typical wait till project, you don't need to do this. So all this is going to do is make sure that we are running, Ah, website that looks basically the exact same acts the exact same. Now again, just as a quick little refresher. Your front end stuff thes three here, Basically anything in the root directory of your rocket man assets that in that were in the zip file can go straight into your route project as well. So I'm gonna go up a couple directories here where I have my manage dot pie file and I'm just going to paste those in there. And you'll also notice now what I just did There was I showed all the hidden files. So if you're on a Mac, you're going to want to hit command shift and then, period, and that will show all of your hidden dot files. We're going to want to copy these as well. This is just a Babel RC file, and a basic editor can fix that. We're using the same type of inventing, so your codes going to look like my code get ignore so that you don't accidentally commit files that aren't really useful for your get repo and the node version that I'm using. So again, I'm gonna throw that into the route of the project. Here. The route is where managed up high lives and then CSS images and JavaScript all go into your static directory here, and you should be replacing CSS and JavaScript folders with the new stuff that's in the dot zip file again on a regular waggle project. You don't have to do this because you'll have your own design that you're working with. But this is just to make sure that you and I are both in sync at the same time. So once you're done doing that, or if you have decided to completely ignore that and work with your own design, that's cool, too. Let's head on over to the next lesson 5. Logging into the Admin: all right. At this point, you should have a base wayto website up and running, but you might not be able to get into the admin area. So if you don't have your website up and running, let's go into our virtual end. Whichever you're using. Maybe it's Docker, vagrant virtue and ven Pippen doesn't really matter. You just need to get into your virtual environment. So I'm going to get into mine using a event. Rocket Man vendor is what is called been activate. I was going to source that file and you can see that I'm now inside of it. And now to run my server going to do Python managed up. I run server and a quicker way is just zero. Colon 8000 and this will run the website on Port 8000. We can see it here if I refresh this page. Port 8000 and it's loading perfectly fine noses. The content management system. We want to be able to get into the admin areas. Let's go into the admin interface and how do we log in? Is it at men? Admission? Is it at men Password. We don't know what this is and by default, Wag Tail and Django and Python. They're all very security based, so you don't actually have a user. There's no way for you to get into this yet. We need to create our own super user. So let's go back to our terminal. Cancel our server with control, see, and I'll clear this. So it's back up to the top. And let's type Python managed up. I create Super User All one word I was going to ask me for a user name. I don't want to be Caleb calling. I just want to be Caleb. My email address is going to be Caleb at Learned wag tail dot com. Password is going to be whatever I want my password to be. It'll ask me to enter it twice because it's a good practice. Let's just go ahead and rerun our server once more. So what that did was it basically just created and admit account for us? So now we consign it. I have the user name of Caleb, and my password is whatever my password was. And wallah, I am inside of my way. Tell at men, and if you ever lose the link to your admin. It's always your website slash admin. That's the default one. You can always overwrite that a little bit later. And if you're just installing your first website, you can simply click on admin interface and it will take you there, and that is how you create a way, Tell administrator Super User. 6. Wagtail Walkthrough: Let's take a quick walk through through the Wag Tail CMS had been. Now this is very bare bones. I just started a brand new project, so there is literally nothing that doesn't come out of the box right now. This is everything that comes default with a brand new way. Tell website now out of the box, Wait till comes with many different components, but most of it can really be split into about five pieces, I suppose. Eso you've got pages which are here, you've got images, you've documents, you've got settings and then you've got users. But to simplify that, I like to really say you've got pages, images and users. Those are the three big ones. So this is your user interface. That'll tell you how many pages, how many images, how many documents you have you can explore through your pages And here if you have other pages, there will be a little arrow here. You can click into that and creates a nice little menu for you, or you just click like I want to edit the home page, Click the edit button and this will allow me to edit the homepage. There's a title field here. So this really only has one field, because again, this is a base. Wait till project. Ah, we can delete the page on Publish it Publishing submitted for moderation cause there's moderation available with Wag Tail so you can have a group of editors who can't publish stuff without approval. And we can also preview our page at any point in time. That's our content tab. In our promote tap. We have a slug. So if you wanted to be your website dot com slash about dash me or your website dot com slash contact for a contact page that's going to be your slug. Here, you can have a custom title that overwrites the default page title. Some show in menu options, which sometimes helps you create a navigation bar. Depending on how the developer set it up, you have a search description. So this is the search description that Google used to use in search engines used to use and still a good idea to have a search description in. There might not be available. Text on your page that might not be visible, I mean, but it could be in the head part of your page. Next, we have settings. These are scheduled publish settings a date to go live in a date to expire. For this, it requires a Cron job. If you're not a developer, a Cron job is basically a task that runs every X number of minutes or hours or days, and it will automatically check to see if you have a page that needs to go live or page. It needs to expire. This is really great for promotion. So if you have a page and you're trying to do, ah, Black Friday deal and it's literally just black Friday, you could say Black Friday for a 2019 was Friday, November 29th and we gets at the time and then the expiry date is going to be that exact same time, exact same day, but at 11 o'clock at night, You do, however, need that to be set up on your server. That Cron job needs to be set up by someone who has access to the server. This is not automatic. Then we have a view live button up here. This is what our page currently looks like again brand new website, so there's nothing on there. We have different privacy modes. So is this page public to everybody? Is it private but accessible to only people who are logged in? We can say yes to that or private, but accessible to people with a following password. And then you could set a password for or is a private and accessible to users within specific groups. Well, we only have the two groups here. We've got moderators and editors. We could create other groups as well and say only certain people can view this page. Next. Let's quickly take a look at search. I'm just in a search for home. Yes, I want to leave the page. It's gonna find the pages for me. It'll find images, documents, users, all other things that it can sort of look for. It really only found one thing here, found a home page called home, and here we can edit the page we can view alive. We can add a child page. We only have the one page type available, so it's gonna ask us to create a home page under homepage. That's a little awkward, so we're not going to tackle that too much. But if you have pages. And maybe you created a page in the wrong place. You can move them. You can copy them, you can delete them unpublished. You can even look at page revisions on this page is and have any revisions. But if I changed any text on the page, if I change any of the content, it will show up in my revision section. Here and again, this all comes default with white tail. Next, we have images in here, so let's go ahead and add an image. So I'm just going to upload an image called Rocket Launch One. I believe this is courtesy of Space X. Upload that and done just like that. We've got an image in here. We get a you, Earl, so we can copy link location pace. I didn't a new in a new tab, and you can see that this image is pretty big, actually, but there it is. So we've got image hosting built in automatically. You can change the image. You can add tags, so if you want to search for it, you can add tags. You can change the title of it and wait till comes with this really cool idea of a focal point. Focal point is, you know, when you upload an image of a bunch of people and, like someone's face gets cut off and you don't want that to happen well in Wag Tail and you can select the part of the image. That's most important then, whenever Waittil tries to crop the image for whatever page you're working on, it's going to get as close as possible to this section. You're saying this is the important part. Move the image over here. So when it crops, it gets a little closer. It's really, really nice. Go ahead and save that image, and that's really all there is to it. I guess we could also delete the image or remove the focal area if we wanted to. Let's move on to documents. Documents are basically the exact same thing as images, so I'm going to add a document in here. Rocket Man assets dot zip doesn't have a preview because it zip file. Let's go ahead and update that click on documents again, and we can see that we have a document in here called rocketman assets dot zip. I can download it directly, or I can click over here. Grab this link. So this is a link Copy link location. Let's paste that in here, and sure enough, it tries to download it again. So now we have file hosting. One thing to keep in mind is these are not private, so don't host credit card numbers or anything like that on here. That is definitely a bad practice. Next, we have settings, inner settings. By default. We have users, group sites, collections and redirects. I have one user in here called Caleb. I've gotten email address first name, last name password. I can change my password in there, and I've got roles. Moderators and editors. I'm not going to change any of those. I am a super user. I mended men. I have more rights than both of these, so I don't need to change any of those going back to users. You can always add a new user. And in here you can say if they're an administrator or not an administrator or if they're not an administrator, should they just be part of a certain group? Groups come with certain permissions, and there's password rules, all sorts of good stuff. Okay, let's talk about groups. We've two groups in here. Editors and moderators. Let's take a look at editors. What can an editor dio can access? The way Telemann cool but cannot add redirects or sites or collections groups or users can't change? Any of them Can't delete any of them. They're page permissions, they can add. They can edit, but they can't publish the can't bulk delete, and they cannot lock pages. Collections are basically like folders for your images, so you can put all of your images or documents in inside of some form of collection. And then you can add permissions around that as well. Images have collections as well. We've only got the one collection called Route, but the exact same thing applies there. All right. Next we have sites so you can have multiple sites hosted on one single instance of wait til Right now we just have the one called local host Port is 80. I'm gonna change up to 8000 because that's what my website is running on. So if I do this local host Port 8000 that's my website comes in change. That's to 8000 just to match it. Ah, site name I'm gonna change us to rocket men just because that's what this site is going to be called, and you can even change the home page for it. So let's choose a different page. We could We can't because we only have the one page because this is a base install of Wait till if we had another page, maybe a different type of home page. We could swap that out entirely. And is this the default site? While we only have the one site. So yeah, we're going to say that's the default site. Next, we have collections again. Collections, air, really just folders where you can put images and documents in there. And then you could put permissions around that so certain groups of users have certain access to certain collections. I'm not going to add a collection because it's not that useful to me at this particular moment. Ah, summits and move on to redirects a reader X A really nice. You can say anyone who's lining on, Let's say your website dot com stash my 2019 offer. So maybe this is some sort of landing page, and I want to redirect them to a different landing page, a different service, maybe a different website entirely. I could redirect everyone to google dot com google dot com. So anyone who goes to my 2019 offer actually, this is not going to be there. Anyone who goes to my website dot com slash my Dash 2019 dash offer is going to get redirected here, and you can set up all these different redirects if you want. You can even choose a page internally inside of wayto to redirect to if you wanted to. And lastly, we have account settings. You can set a profile picture. You can change your email, change your password notification preferences. There's language preferences. If these are all enabled, which they are by default, you can set your current time zone Micro and time zone is not even set, so I probably want to go and set that a little later and you can even change your name. So these are all the base things that come with Wait till right out of the box, and every single component is extendable to some degree. So, yes, we only have one page right now, but we can add hundreds of pages later if we wanted to. Sure, we only have one image. We could add more if you have a contact page. If a developer has made a contact page for you, you'll see form submissions in here as well in your menu. If you have snippets enabled to reusable pieces of data, you'll see a little section in here in your menu called snippets, and you might see other custom things that developers will put in. There's bone, but out of the box. This is what you get with my tail. 7. Editing the Home Page: All right, let's go ahead and edit our home page. It's actually work with some code here, so I'm going to get into my virtual environment. I'm using vendors, so I'm gonna source my rocket man ven been activate and I am inside of it. I just want to run this website. So I'm gonna run Python managed up high run server on Port 8000 and we can see here if I refresh, it works. But if we actually go to the home page and try to edit something on here or even if we try to view the life page, we haven't done anything yet, So let's let's actually get into this. So we're gonna add a number of fields here because right now we only have the title field in our main content. We've got promote fields and settings field, and that's great. Wait till gives us all of the's options, but we want to add more. We want the home page to have a little more spice. So let's add like the home page banner. Alright, so I've just opened my editor and I've opened up the project, so I've got all my files in here you can see them on the left hand side. I'm just using V s code. By the way, you can use any editor. Doesn't really matter. So I got my front end stuff in here. I've got a home app. I've got media. This is where my images and documents and stuff We're going to be uploaded. Uh, not rocket Man. This is where my setting, static files and temples were going to be. We're going to be doing a lot of work in there. We've got the virtual environment, which we don't actually need at all. It's not for editing purposes, and we have a search app which we're not going to be using. So the first thing we want to dio is open up home and go down to models dot pie, and we're going to see in here. There's actually nothing in here at all. And if we take a look for base dot pie and we scroll on down, we see installed APS, we have home and search and a bunch of wayto ones model cluster tag it and a bunch of jangle APs all installed. This comes default with your wag tail site. The two that we want to pay attention to hear our home in search and actually more so we want to pay attention a home. So this home H o M e matches this app right here. And if we wanted to get rid of home page, we could just comment that out or delete it entirely. Save restart our server and we won't have access to the whole map anymore. We'll get more into this as we go through this course. But if you added another app in here, such as services, you just added in there like that we'll talk more about this little bit later and and how to start a proper Django and leg tail app in our home slash models dot pie, we have just a single class called home page because the APP home is enabled. Wag Tail and Django say, Oh, this is a classes is a page. Okay, well, I'll automatically provisional little bit of space in our database and structure properly for your homepage and passed just means it's not doing anything. So what we want to do now is we want to add a few fields we want to add maybe some lead text. Ah, button button text and maybe some background image for a banner. So let's just do this one at a time. Let's do lead text and this is just going to be a very simple char field, a character field. So this is getting into jangled. And so this isn't wag tail. This is Jenkin. We can tell because this is coming from models dot We can see if we look at the imports here is coming from django dot db import models. So we're going to create a new char field or a car field. V s code is nice enough to show me all the different options in here. The 1st 1 that we really want those max length were instead of maximum length of about 100 40 characters just for this lead text, we're gonna say it can in fact, be blank. And maybe, let's add some help Text help taxes equal to a subheading text under the banner title. Something like that. We can always adjust these later as well, and I'm going to clean this up a little bit. Put these on separate lines so it looks a little nicer and just like that. We have a new field. We have to run some migrations and stuff, which I'll talk about in just a minute. Let's go ahead and add our next field. Let's add a link to another wag tail page. Now we only have the one wag tail page, so this is not going to be super applicable right now, but we're going to see how this works. So let's create a button. And for this, we're going to use models dot foreign key. We're going to select a foreign key from the WEG tail core. Dot page Blank is equal to true. We're gonna say that's optional. No is equal to true, so it can have absolutely nothing in the database. Let's give this a related name of just plus basically ditched the related name. For the most part. Help text is going to be Live that up. Select an optional page to link to this is going to be for R C t a button. We don't really know what that looks like right now, but I'll show you in just a little bit and we need one more in here just to satisfy Django , cause again we're still working with Django here on delete. What do we do when whatever Paige this is being linked to is deleted? What we're gonna say, models dot setting all and let's create some button text again. This is just going to be regular char field, so models dot chair Field give this one a max length max length of 50 so the button text cannot be too long. Let's give it some default text, though Read more can be blank. We're gonna say no. This always has to be set, even if there is no button selected were going to say this button text has to be selected. There has to be filled up and let's add some help text button text and you'll see what all this looks like once we get just a little bit further. Now, lastly, we want to add an image in here. Let's do banner background. Image is equal to models out. It's another foreign key, but this time we're going to be linking to wag tails. Images dot image. This is their default image, so if you're using a custom image model, you're going to use a different value in here. But we're not. We're just going to use wag tails standard images, so we're going to link to this one. Blank is equal to false, but no is equal to true. Which means in the database this field, the column called Banner Background Image is allowed to be empty and blank means When you go to save the page, it actually has to have a value in there. So in the database, this can have nothing. But when you fill out the form, it has to have something. That's the difference there related name is equal to. Plus Cousin was gonna throw that away. Help text the Boehner background image, I suppose, is what we can say and on delete. What are we going to dio? Let's say model is dot set No. Now let's go ahead and save this Open up our terminal. It restarted. Everything looks OK, but when we open up your browser, we are going to see when we refresh our page. It says that there's no such column. Home is the APP. Home page is the class name. Lead text is the column name in a database. So how do we make this existing database? Well, we're not going to do anything with the database in particular, we're going to let Django handle this for us. And so this is really, really easy. All we have to do is clear. Let off. Let's do python manage dot pie make migrations. And what this is going to dio is really just create a set of instructions for how we want to restructure our database or how we want to alter the database. So it made a new migration for home called 0003 auto blah, blah, blah bunch numbers in there. And if we open up our editor go into our migrations file, we'll see 0003 auto, blah, blah, blah. And as a bunch of instructions in here, you don't need to know all this stuff. Django handles all of this for you, which is really, really nice. But at this point, all we have is a set of instructions. We haven't actually told our database to use this set of instructions. So our next step is going to be python manage dot pie, my great, And what this does is it takes all the untracked migration, so it's going to store which migrations have already been applied in the database. That's going to say, 00003 Auto in the home app has not been run. Let's run that. Let's let's take all the instructions that we see in here and that's alter the database. And so let's do that. He had enter. The migrations were run. Okay, and now we can rerun our service. Let's do Python managed up. I run Server 0 8000 Open up a browser again, Give us a refresh and our page loads. But we have a problem. We wanted tohave lead text, ah, button, where we could select another wag tail page, some button text and a background image. And then, if that's showing up, it's not in promote. It's not in settings. It's not in content now. Why is that? Well, there's actually good reason for this is just because you have set fields in your model. Just because you want to alter your database and store some data doesn't necessarily mean that you always want that data or that field to be exposed. You might want some data in here that can only be programmatically changed, and so because no Web app on the face of this planet. I can tell you what you're planning on Lee. You know what you're planning. This is not going to expose any of these fields for us. So we now need to expose these fields and in wag Tail. This is really, really easy. So on every page and I'll just scroll up here, you can see that I'm still on the homepage. On every page we get content panels is equal to page dot content panels. This is a page class provided by Wag Tail That gives us our title and whatever defaults weg wait till wants to give us. And these are the content panels. And so we're just gonna say, Give us the existing content panels. Plus, let's append a list to this. Now we have different panel types in here, so we have to make sure we use the right one. If we use a field panel that's just for regular text. We've got lead text in here, and that's just a regular text panel. Wait till will figure out that it's a char field and it should just be one line of text. It's a regular html text input the next one for a button is going to be a chooser panel and we'll have to import these in just a second. And that's all we dio. And so this lead text matches this lead text up here and this button matches this button right there. So now we need to add. But in text and banner background image, let's go ahead and add one more field panel. Field panel button text is what it was called. And then we're going to add one last one image chooser panel banner background image. Now if we save less and check out our terminal is going to complain. Yeah, it says field panels not defined. If you ever see this ever, it's just saying that basically what you're looking for has not been hasn't been imported into your page yet or into your python file. Let's go ahead and import these from wait. Tell dot admin dot edit handlers that those panels those air called edit handlers Let's import the field panel and V s code is nice enough to try to auto feel this for me Field panel and page chooser panel and the image chooser panel actually doesn't come from Edmund at handlers. It comes from wayto dot images dot edit handlers Import image Chooser panel say that it looks like everything here is working nominally, which is great news. If, for whatever reason your server has died, you just need to rerun it. Just run. Python managed up. I run server 0000 8000. Let's go back here and refresh your page and look at that. We have lead text button button text and an image. This is pretty nice. So we're gonna change our home title to rocket and maintenance because that's what this website is going to be all about. Maintaining rockets. Let us handle the dirty work. You focus on launching into space. Something like that. A button. This is pretty nice. This gives us a what's called a page chooser panel. So if we had more pages, we could go and explore more. We'll see more of this in the future. We really only have the home page right now, so actually we don't need that at all. So it's clear that choice the button text. We said it always has to be there, and the default is read more and a banner background image. I'm going to upload a new image here. Hit, upload Cool and let's go ahead and publish this page. Now when we view life yet nothing has happened again, Wade tell is not a 10 minute website content management system. This is for customized websites, and at this point again, wait till Django and Python don't know what you're looking for. There's no pre defying theme here. This is just what wait till came with. So now we need to go and edit our home page itself. So it's open up our editor. I'm gonna open up V s code. Let's go ahead and close that file. We don't need it. And if we look in our home app and then in our templates directory in there, we've got a home folder which has a home page and a welcome page. The home page is the one we want to edit in here. It's extending from based on html. We'll talk more about based on HTML. I believe in the next lesson is going to load static. It has a body classing here, some extra CSS if we want that and where we want to put our main content. So just as an example, let's do this H one hello world And all we're doing is overriding this content because before what we had Waas it was including home. Welcome Page was including all this stuff in here. We don't want that. So we're just gonna say Hey, overwrite it, save it. Miss Refresher Page says Hello, world. So now we're actually editing our home page template at this point, which is pretty great news. Let's go ahead and get rid of some other stuff in here. We don't need extra CSS at all or body class. We may decide to introduce that Ah, little bit later. Reintroduce that a little bit later. But for now, we're we don't need that. We just need to change the main content section and because we're not using the static tag , we can get rid of this. We'll see a little bit more about static a little bit down the road as well. So let's see how we can get the page title. So we want to change hello world to say, rocket maintenance. That's that's the title of this page rocket maintenance. So we could just do Curly bracket, curly bracket page dot title. And if we refresh our page here, says rocket maintenance, that's pretty nice. Let's go back and let's see what some of our lead text looks like, so page dot lead tax. Now, if you're wondering, Caleb, where are you getting lead text? Where you getting title from? Let's go back to our models. Lead text is defined here, so because we're on a page template, you can use page dot That's the page object, and then you can use lead underscore text. That's our custom field here. Lead text. And if you're wondering, OK, love okay, yeah, that'll make sense. But where did you get title from? That's a great question. Wait Till comes with a lot of goodies already built in. So if I right click here, go to definition. Not every editor has this feature, by the way, but this one does, which is really nice. You can always check out The source code is well, get hub dot com slash wag tail slash wag tail. This is the page that we're using, and it already comes with a title field. It's got a regular charge field in there with a verbose name. Max length help text. It's got a draft title slug. It's got a bunch of other things in here, but we got title because we're using this class in our python class. So in the world of Python, we can inherit from other classes and get all the goodies that is going to give us. In this case, we're inheriting waggles page class, and it comes with Title, which is pretty much the exact same exact same thing as us saying Title is equal to char field, yada, yada, yada all the fields in there and then accessing it on her template with page dot title. All right, let's say this and the see what our lead text says. Look at that. We've got to delete text in there. It's tiny and it's not styled it all, but it is now pulling in our custom fields. All right, so what I'm gonna do here is delete this and I'm going to put im in a paste in the front and stuff, and then we're going to at layer in the wait till template part. All right, so I just pasted in some front end stuff there. I haven't done anything with the wag Tail work we've done yet. So if I preview this yeah, it doesn't look great. We'll add the styling and stuff later. I think it's in the next lesson. We're gonna add all the styling, so this looks really good, but for now we want to work with some of this logic. So, like our page title up here should be whatever our page title actually is so we can use curly braces for the mustache Syntex title. We've got lead text in here. We can do page dot lead text. We've got button texting here we can do, do do do do do do do page dot button underscore text and you for wondering where I got that one from that comes from but in text We also have lied text here, which is allowed to be blank, which means this is optional, so we need to make sure that this is optional. Now we can do some if statements in here if page dot lead text and if and this is just regular Django template ing, you know, if your brand new toe to Django and wag Tail, this is basically how we add logic into our page. We used a curly bracket, followed by a percent sign, and for the end of our logic, we use percent sign and then curly bracket. And if we ever just want to display information, we just use to curly braces together. So let's go ahead. Preview this. We can see you rocket maintenance. Let us handle the dirty work. We need to check to see if that button actually exists. Set that girl and maybe a background image. So before we even say that there's a button, let's go ahead and do if page dot button and again, I just got that from do to do to do or foreign key. And I'm gonna invent this because it looks nicer that way. And for the link, we'll give you page dot button that you are out because Button is a foreign key. So this is going to link to another page. Maybe it's Page I d. Number four. That's what is going to link to because that's how foreign keys typically work. So is going to again Look up Page I d. Number four with this page dot button, and then he's going to give us our u R l whatever that's going to be now that's not going to show up with anything. So if we preview the page, are but it actually goes away, let's change that. And let's just set the home page publish Angela. It shows up hard to see. We'll fix that in a bit, but it shows up and the link. If you look at the bottom left, says local host Port 8000 that is, in fact, the homepage. Lastly, we need to set an image in here. And so this is the first time we're going to be working with images, Certainly not the last time we'll talk more about images in depth as we progress through this course. So the first thing we need to do is we need to load. What this is saying is, Hey, when you load this template, Django, when you load this template, know that it extends based out html. We'll talk more about this and blocks in the next lesson. Ah, and there's a section in there that we want to overwrite called content, but also just so you know, we need to load way, tell images, tags because we're going to be using a tag that Wag Tail gives us and that allows us to use a template tag called Image. And we tell it as its first parameter which image we want to you. So let's do banner background image. Our second option is going to be How do we want to create this image? It's going to cropper image. No matter what we dio do we want to fill it with certain with and heights. Do we want to say there as a max height or Max with anything like that? We'll talk more about that later. Again, I just want to show you this sort of just to get your feet wet with images and we're gonna say Phil 1600 by 800 as BG image. And now we have access to BG image as a variable. So let's type curly braces. BG underscore image dot u R l Let's refresh our page. We'll see that we have a u R l up here. If I go ahead and copy this paste that into our browser. Look at that. It made the image for us. It cropped it. So this was originally a square image I believe and how it is 1600 by 800 and so just handled all of that for us, which is really, really nice. Now we need to take this u R l and apply it as the background. And so I already have some styling in here and I want to add this background image you are . L save that refresh and we consort to see that the images sort of there a little bit again . The styling will come in the next video, I believe where this is actually going to start looking like a website. There are also other fields that we can add to our model. There are things like Stream feels that we can add There's validation and things that we can add which we will add a little bit later. Just not right now. At this point in time, what we've done is we've modified the default weg tell home page we've given it for extra fields, so it already comes with things like title. We've said we want to give you a lead text, a button button texts and a background image. Now, if you're ever applying this and you apply a field panel to either the page chooser or the image chooser. I'll show you what this is going to look like in the ad men when we edit our page. Our button now becomes a drop down on all the pages in our wait till site, and the image chooser also becomes a drop down. Of all the available images that we have on our website. That's not really what we want. That's not a nice user interface. So if you ever see that, chances are you're probably just using the wrong chooser panel. Field panels are great for just regular text page shoes or panels are great for foreign. Key is going to another page as we see here, Foreign key going to another page, we said this could be any wag tail page, any future wag Tail page, we could create a swell another text panel and then an image chooser panel for our background image. And again that just allows us to choose and upload images sort of dynamically, which is really, really nice. Next, let's go ahead and work with the base template and make our website actually look like something 8. Working with the Base Template : in the last lesson. We worked with the homepage and the home page template a little bit, but let's go ahead and actually structure based on HTML template. Now, if you don't know what the based on HTML template is, let's actually look at that first. So I'm gonna closed down the files that had opened from the last video open up base dot html and you can see it's in my Rocket Man folder slash templates. So if I close all of these and reopen this one, we can see I've got Rocketman templates and then based on HTML. And the idea is that every single page eventually gets derived from a base template. So instead of having to declare HTML five class node.js title meta data all this other stuff, we don't have to do that every single time. Instead, what we can say is, Hey, everything else is good, it's fine. But let's just override this content block or let's just override this body class block. There's no need to add JavaScript every single time because we can just add it into our based on HTML, and it just knows. And so as an example, if I open up home page dot html. We have a section in here called Block Content. We also have a section in our base dot html called block content. And so those two match, which means anything in here because we've said it. It extends based on HTML. Anything inside of our block content. All this stuff is going to overwrite Whatever isn't here. It just happens to be that there's nothing in there right now, but we could give it a default. All we have to do is say hello world, and this will automatically be over written on any page that is using this block content section. So there's just a couple things I want to clean up in here just to make this website a little better. We're not going to check to see if JavaScript is enabled or disabled, although you might want to keep that. We're not gonna worry about the title or or the description or anything like that. For now, we have a global style sheet, and this is looking for static. CSS rocketman dot CSS Now A couple lessons ago we saw this loaded in here says Load static , and we said how we don't need it. We're not going to use this time. We are. What this does is says Hey, in the jangled wag Dell'Apa, you have a static folder somewhere, Whatever you named it, because we can change the name of that folder. We can even actually specify that the folders, not local, could be stored on Amazon s three or digital ocean spaces or something like that. But what we're saying here is check the static folder. Then there's a CSS folder, and there's a file called Rocket man dot CSS. Now, let's see if that actually exists. So in a Rocket Man folder, we have a static directory, actually just called static in, Which is funny way of naming that. Then we've got a CSS directory, and we've got bootstrapped out CSS and bootstrapped on CSS dot map. So this isn't called Rocket Man in our project, this is called bootstrap dot CSS. Now, if you don't have bootstrapped on CSS in your project, you're going to want to go back a couple of lessons and download the project files. I have actually provided these files for you already, so you just have to copy and paste them into your static directory. So if you don't have those, you're probably gonna want those. And then we have some global javascript down here, so let's take a look again. This is saying the sources static wherever that might be. We just know that it's right here. Fuller called Js. And this one is rocketman dot Js. Nope. For whatever reason, I decided to call that index dot Js and that matches right in here. Let's go ahead and give this a quick save and a preview. So I'm going to save that file, go back to our homepage and Walla. It looks not bad. We don't have anything else to display on here, and that's okay. But we've gotta wait till user bar, which is really nice, because if you want to add a child, Page added this page showing explored or the wag tell a man you can do all those things and that only shows up if you're the admin. And if the wait till use a bar is enabled, which is right there. So if you ever wanted to get rid of that, you just delete that save refresh and that user bar goes away. I like it being there, though it's pretty convenient. Let's go ahead and clean up some stuff that we don't need on this website. But, you know, it might be useful for you in the future. So if you ever want to modify your body class, you might want to keep this blocking here. Because, for example, if we go to our home page, we could throw this in here and we could call this home page something like that. And that will essentially do this when the home page is loaded. But for this website, we don't need that because this website is very simple. So I'm going to get rid of that just because we don't need it. And then for our content section, I actually just want to wrap this in a main elements domain. Give us an idea of content. Ah, the classic Do some bootstrap classes here. So, container margin, Why? Access for And the role is going to be main nice and simple, and our content is all going to live inside of there. Go ahead and preview our page and oh, okay. Well, that sort of broker banner, you know what we can do is that we can actually create a new block in here. Let's create a new block called Banner So Block Banner and by default is going to have nothing in there. And let's end that block. And what this is going to do is allow us to use this block on her home page, and it's not going to be wrapped with whatever bootstrap styling is going to come with the main element. We're going to actually bypass that and inject it into here instead of into here. And this is a really nice part. All we really have to do is change this one word here, two banner. So this now matches that, and at the end of our block, we can give it a name or we don't have to give her name. I like naming it. Sometimes I don't name it. It's sort of optional, but we're gonna change out when Do Banner as well. And so if we zip this up, we can see Block Banner a bunch of stuff inside of it. It's 25 lines of code in there, and then we're ending our block banner. Let's go ahead and save that refresh our page now our banners up top and we have a content section down here. So if we inspect our page due to do, we can see we have a Jumbotron in here. That's our banner. That's part of this part up here. Ah, and then we've got a main section scroll down. We've got a main section down here. This yellow highlighted parts. But there's nothing in it. But that's where we're going to put our future content. So that is working with Based on HTML, we have modified the home page a little bit as well. At this point in time, we can also go back to our home page and do a little cleanup. We're not using the welcome page at all. We can actually look through her whole project for welcome page dot html, and we've got one in here that's coming from our site packages in a virtual environment that's just the default, one that comes with Wag Tail and the other one that comes default with Wakil. That's not in our project, not what we see in the left hand side here. So let's go ahead and delete. We don't need that anymore, and there we go. We now have a homepage with a banner. It's got a background image. It looks pretty nice. We've got a title of best lead text and we've got a button. A call to Action button. 9. Services App and Pages: up into this point, we've created a home page. We've modified the based on each email template, and we've loaded in some some new JavaScript and CSS, and now we want to add some other page type. So let's go ahead and create a couple service page text. No, for this we are going to need to. Source Are rocketman ven have been activated? Get inside of it if you're using Pip and it's just Pippen show. If using Docker, it's docker exact I t. Whatever your container name is. Bash, That'll get you inside of it and then you can run. Then you can run. Python managed up. I run server for 8000 40,000 just the default port that US developers tend to use. It's actually nothing special about that. And now when we refresh your page, okay, our servers working. And at this point, we actually want to create a new app. So we could theoretically, I don't see why we couldn't. We could put all of our pages into home slash models, not pie. That's a weird naming convention. We're saying all of our pages need to be in the home APP services need to be in the whole map. Contact Page needs to be in the home app. Miscellaneous page needs to be in the home app that doesn't make any sense at all. So we're going to create a brand new app, and this is actually just a jangle management commands. Let's go ahead and cancel our server and we're going to type. Python managed up. I start up services. And if we take a look in our editor, we now have a folder in here called Services. Has a bunch of files in your views. Tests models absent Minute Dot pie and Migrations folder with an empty and it dot pie file in there as well. That's a standard jingle app. What I'm going to do is delete a bunch of these files. We don't need views. We don't need tests and we don't need at men. My tail takes care of a lot of that for us and the tests. If you want to write your own test, absolutely, you can. We're not going to be doing that because we're not doing anything super crazy With this wag tail site, the remain one is going to be models dot pie and We're also going to want to open up our base top high. Not that one based on pie. And where we have our installed APS dress underneath. Here, let's go ahead and give it services. Don't forget to put it in apostrophes or quotations with a comma because this is a python list. And this name, all this does is reflect this name right here. That name, these two names they have to match. So I'm gonna save that file, Closed that down. And I am back in services models, not pie. So, in a regular Django app, you know is that I deleted the file called views dot pie, an irregular Django app. You have models, views and templates. Well, we're gonna keep the models file, but the view we don't really need that with a wag tail site because a wag tail page will combine the view and the model at the same time. It's actually less to manage, and in my opinion, it is a really, really nice thing. You can see everything that's going on on a page in one single file versus in a Django application, you have to switch between your models and your views. We're also going to keep aps dot pie just for a good practice. Keep your innit dot pie file that tells Python basically to include the other files in here , even if it's empty. And keep your migrations and the Empty Unit file inside of that as well. Next, let's go ahead and treat to new page classes. We want to create two new pages, so class service listing page is going to be a page. We have not imported this yet, so let's do pass and let's do class service pages, just a standard page. We don't know what's gonna be in that either. So let's also pass. This does not exist. This is not a variable. This is not an import. This doesn't exist yet, so we need to make sure this exists. So from Wag Tail Doc or dot models import page. And that's really all we have to dio. So now, at this point, we could run python, managed up high, make migrations. We could also run python, managed a pie migrate, and it would create a migration file that has the service listing page and the service page in it. And then we could start actually adding these pages to our way telling men were not going to do that yet, though we're going to add some stuff to this first. So our service listing page Let's go ahead and add Maybe like a subtitle. So we already know that Wait till comes with the title field That's inside of page. We don't need that because it's already there. Let's had a subtitle, though, and we're gonna say the subtitle could be fairly long. Let's do models dot text field can be blank. Yes, we're going to say that basically, this is optional. This does not need to be there at all. So when you save the page, can the subtitle field be empty? Yes, that's what we're saying. We're gonna say max length of at 500 character, something like that. Now, remember when we runner migrations, this is not going to show up on our page when we go to edit a page. Yes, this will be there in the background, but it's not exposed, so we need to expose it with content panels. We do this by saying content panels is equal to page dot content panels, so we're going to take whatever wag tails already giving us. That's the title field. We know that much, and we're going to append a list to this. And in here, we simply just want a field panel for the subtitle. And again, this subtitle Field name matches this one in the Quotations. And it doesn't matter if it's quotations or apostrophes that just needs to be some sort of string type, just as an example that switch it, it doesn't really matter. They both worked perfectly fine, so that's cool. So our service listing pages Now we're going to have a subtitle now in our service page. We're going to want a little bit more. This is going to be a detail page, and so on her detail page, maybe we want some sort of description. Maybe we want a link to some sort of internal page as like a call to Action Button or maybe a link to an external page, something that's outside of our wag tail site. That would be the U. R L. To a call to action button. We're gonna have ah called action button. So we need somebody texting there, and that's ah, it's also give us an image service image, so we know we have five fields in here. So the description this could be somewhat longer. So let's do a text field instead of a chart field. That's the difference. HR Field. This one is typically used for shorter amounts of text and text. Field is used for larger amounts of text. Blank is equal to truce. We're gonna say that's optional and a max length of 500 characters. Now we've got internal page. We did this with the home page already. We need this to be a foreign key so foreign key to any type of wag Tail page Wag Tail core dot Page. And this actually looks a lot like from wechsel core import page. And if we open up our base dot pie, we can actually an app in here called Wag Tail Core. So what this is doing is it's looking through wag tail core, and it's looking for a page model. That's what we're going to be able to link. Teoh Blank is equal to true. We're going to say this is optional. No is equal to true, so it could be stored as absolutely nothing in the data base just a no value. The related name for this particular purpose is not important. So we just use a plus symbol, help text select an internal, wait, tell Paige and lastly on delete to satisfy jangles needs. We're gonna say models dot sentinel. We could do that because no is equal to true Sentinel is allowed. Next, we have an external page, so maybe someone has called action on their page, and that button is supposed to go to a different website. Maybe it's app dot my website dot com instead of just my website dot com slash sign up. Who knows? There's lots of different reasons for this. This one is really easy. This one is just a ul field. I'm gonna say blank is equal to true and we don't need to specify no is equal to true, because when it comes to regular text fields, all we're going to dio it's a either it has an empty string or it has something in it like that. We're not going to say it could be No, that's just a good jingle practice. We've got some button Texan here. We did this with the home page as well so models Dutch Hartfield Blank is equal to true. Do we want that button text to be optional? We could do that. I don't see why. What we couldn't do that we can always check to see if there is, but in text. Otherwise supply default text. And because it's HR Field, we do a max length of 50. That's pretty long for Button, but we've got that and then a service image. This one needs to be an image. So we do models dot foreign key, and this is going to link to wag tail images dot image So again that goes to the wait till images app and is looking for the image class. We're not using a custom waittil image. We could specify our project to use a custom. Wait till image, but we're not. We're just going to use the regular wait till image right now. We're going to say no, it could be true, So it could be absolutely nothing in the data base, But blank is equal to false. So whenever you save the page, you need to have an image selected on delete. And this is just an example of how these property names don't really matter the order, while the 1st 1 has to be there. But these other ones these air keyword argument so it doesn't really matter which order there in So on. Delete is equal to models dot I said No help text. This image will be used on the service listing page. I just know that because I've pre planned this and will be cropped to, I think it's something like 570 pixels by 370 pixels. We'll probably also use it on this page as well. Yes, it will be cropped 5 17 Ah, on this page, There we go, something like that. It's just helpful. Text is what it is. And of course, we need a related name of a plus sign, and that's it. Next, we need to add our panels. But let's do that in just a sec, because I want to show you a quick little thing here. So let's open up our Edmund and it's not currently running. Let's run Server 8000 field panels not defined. Okay, Cool. That is expected from Wag Tail dotted men dot and it handlers import field panel. We'll need more of these later that will satisfy this particular error models. That foreign key is not defined. Yup, that's because that would have been a typo. Models dot foreign key. There we go. Related. Named again. This is just catching all my terrible, terrible typos related. Named is just related name. I had to manually restart that one. No errors. Okay, cool. So now let's open up our admin. We have a page in here, We have rocket minions, We can add a child Bage, and we can add a home page, a service listing page or a service page. So let's add a service listing page. Oh, no, we got a 404 and this is the air that I wanted to show you. The wag Goldman is going to try to gracefully handle this problem. Basically, what this is saying is on this page, this isn't actually using any data from the database. This is just going through all the registered APS and checking for pages which in this case , is going through the service app. That's the name here, services AP and is looking for a service page and a service listing page. And so it's not actually using anything from the database, But at this point, we click service listing page and it says, Oh, no, something broke because it's asking for data from the database and it doesn't have it yet. We have not made any migrations. So just like on the home page, we're going to create some new migrations. So it's you Python managed up. I make migrations. And again, this is just going to create instructions for our database. Next, we need to do manage that pie migrates to actually apply these to our website. Let's go ahead, Runner server once more refresher page and wait for it. Bam! There it is. We have a service listing page. We're gonna call this services. We can give it a special slug. Call it services, I guess. And a subtitle. Here are all the services we can perform on your rocket because apparently this is a start up with a lot of money. Let's go ahead and publish that and we can view this and we see template does not exist. Now let's just hold that there for a second to come back to this in just a moment. I'll leave that tab open when I come back to here. I can now actually go into my pages. I got rocket maintenance, A page underneath that is the services page. All your pages should live underneath your home page, and then we've got an edit button. So I go back. We've got Arrow because there's now child pages and we can view the services. Now let's go ahead and add a child pager. That's Atty Service Page. But if we look at this, this isn't showing anything that we've actually added. We've added a description, internal page, external page, but in text and his service image. But we haven't added any panels again. Wait Till and Django, they're saying, basically, you have these fields, but we don't know if you want them all exposed, so you're going to have to expose them on your own because again, we don't know the logic behind your application. So you're going to have to manage that, and that's actually a very preferred way of doing things. So I'm just in services models that pie. I'm under the service page and in here I'm going to add content panels is equal to page dot content panels, plus a list so again. We're just gonna say, Hey, let's to find some content panels in here. But let's also grab that wag tail a title one that the one that comes default with wagged. Oh, and now we have to add a few of these. So let's go ahead. Copy all those I m. Pei citing here. So I've got a list to reference from comment That case, it does actually throw any problems for me. So I've got an internal page here, right? I want to use a field panel for an internal page. Is that right? I've also got description for the internal page. No, this is a foreign key to a page. I want this to be a page chooser panel. Otherwise we're going to get a drop down list of all of our pages, and that's no way to live life. So page chooser panel and let's go ahead and add a regular field panel for our description . Next, we have an external page. This is just a ul field. So external page is our your URL field right in here you are all field is really just a text field with extra ul validation, so that could be a field paddle because it's a regular field. Next, we've got button text again. This one is just a regular text field. We can see that up here. It's just a char field, and then we've got a service image. This is a different one because it's a foreign key to an image we don't want to drop down. List of all the images we can select. We wanted to actually have a image chooser motile like a little pop up and allows you to upload one on the fly. So let's add an image chooser panel. And at this point, if I save this, my terminal is going to complain so hard that I'm missing imports. There it is. Paid shoes or panels not defined is going to say the same thing about image Chooser panel. Let's go back up to the top. Let's add paid shoes or panel. We know from a couple lessons Go that the Wag Tail Image Chooser panel doesn't come from the admin at Anders. It comes from the image at, and there's import image chooser panel, and that's just so that all your image stuff stays the same images, air sort of a core pillar behind Wag Tail. Let's go ahead and say that Refresh our terminal. Oh, no module named Wait till image. It's because that's a typo, its images. That's plural. And there we go. Now, when I refresh this page, when I want to add a new service page, you can see it up here. I now have a title, a description in an internal page. I can select another page, and you can actually traverse all the pages in your website. This way we can add an external page. So maybe I want this to go to space x dot com But in text, check this out service image. I'm going to use one I've already uploaded before, called Rocket Image. And what is the service going to be called? Adds a cleaning service. I guess we'll clean. The Rockets will make sure it's nice and shiny and white and smooth, frictionless and all that good stuff and then a description. In here. This is now a text field, so we have multiple lines. We can see that here, would you do to do. We can make multiple lines to. That's pretty nice. I'm just as, um, warm ipsum text in there. Let's go ahead, publishes Page and View Live, and we're going to see the same thing. These templates don't exist yet. Now what this is doing is looking for a service page, and it's actually getting the name automatically. So if we go down here, we can see that's a service page. Wait till Njenga were smart enough to know that this isn't the services app. So it's going to look in a folder called Services and the service page is going to change us into snake case, So it's gonna go service underscore page dot html. That's the default template is going to look for now. If we go back to our error, we can actually see if I can make this larger template does not exist. We've got a nice CRL. So I were website dot com slash services slash cleaning, and we can actually see where it's trying to get this. This template from so first place it looks for is in our Rocketman project in templates in services and then service page. So let's close up all of these. We go into a rocket man, we've got our templates in here and It's looking for a folder called Services and a file called service page dot html. So that's the first Place is looking for the second places, is going to look in the home app because we have an app called home. So it's going to look in there and then is going to look in the search app it's going to look for. I use home as an example. Home has a folder called Templates and then a sub folder called Home and then a Home Page in there. So it's really looking for the same thing. Only this time it's looking for templates, slash services folder and then service page dot html. So instead of home, it's going to be services and then service paid shot. HTML. We don't want that in there because that's really adding for merging two AP together. What we're eventually going to end up doing is we're going to create a new template in here like this. Actually, let's do it now. Services service page dot html. So I've just split my page here. I've got Service Page that each month in the left, I've got home page dot html on the right and again that service pay just lived under templates, services and in service page. That's exactly what Wag Tail and Jangle are expecting. Now we need to copy over a few things we say, Hey, we want to use our A typical based on HTML file, just the generic one. Let's add a new block in here and let's call it content much like our banner. But we just want to add some content in there and in our content. Let's do hello from service page dot html. Let's go back to our page where we're getting this air. Zoom back out. Refresh hello from service page dot html. Now, in the next couple of lessons, we're going to work with this and also the service listing page, which currently is giving you an error. We'll work with both of these and make these pretty nice looking, and we'll get more into the template management side of things 10. Service Page: before we continue adding features and and data fields to our service pages, let's actually make these pages look somewhat respectable. This is the fun part behind but development, and it's pretty crucial to have a nice looking website. So instead of having this page that just says hello from service page dot html, let's go ahead and actually update that. So it looks like a proper service, Pedro. It at least it starts to take some sort of form. After that, we are going to update our service listing page. So I've got the service page open here. How we're going to add a service listing page template, and we're gonna make that look a little nicer as well. So first things first. We need to know what we're working with. While we're working with a service page here. We've got a service page. We got description, internal page, external page, but in text, we've got service image and some panels. Let's go over to our terminal and actually make sure that this is up and running. So let's source our environment. So I'm in a source, the rocket man, then been activate. And there we are. We're inside of it. And then just simply Python managed up. I run server on Port 8000 and when I come back to my ad men, we can see that this works. I can refresh my page and it works. It doesn't give me an error saying that it's not up and running. So let's open up our service page here and let's get the title and description just as an example. So in here could make that bigger as well. In here, we can say hello from page dot title and let's throw a description in here as well. Page dot description. And just as a quick refresher, I'm getting description from the model field, the text field that we created in the last video. So I've saved this file and let's refresh it and we can see say hello from cleaning. That's the service type and a bunch of warm ipsum text that fills. That's exactly what we were putting into our page title is Cleaning and Laura MIPs, um, text for the description. All right, so I just put some front and stuff in here, some html, a little bit of CSS, and it just really makes the page, you know, start to look like an actual page. So if I go back to my browser and hit Refresh, this is what we're going to see. We'll see something along these lines but have an image on the rights, the page title, some description and a button, and that, but needs to go somewhere. So let's go ahead and feel this out with the fields that we've already added. We know we've got title description, internal page and external page Ah, but in text and a service image. So we're going to be working with multiple different things here. So let's just start at the top. We have a page title in here. We want to use the actual page title. So what I'm going to do is just use curly braces, double curly braces. He's title. And for the description, I'm going to do the same thing with the description. So page dot description and these just get wrapped around with curly braces or the mustache sort of syntax. Let's go ahead and save that and refresh our page. We can see this is starting to come together already. Let's go ahead and work with this button texts just a little bit. So we know in our models we have an internal page and external page and some button text. But in Texas, the quickest one to pick off here. So let's go ahead and change that button text. So page dot button text. Refresh your page. It says, Check this out. Is that what it says when we're editing Are paid? Yep. Sure enough, it says, check this out. Okay, Now we need to determine if an internal page or an external page has been given and create a euro. So we're going to take this in a little bit of ah longer approach here. I'm gonna throw some horizontal rules in here just so we can see text in here. And this is where the girls are going to come out just as an example. So first things first, we have that internal page. That's the field name. It's called Internal Page. Now, this is a foreign key to a wag tail page, which in the database, really just means this a stored as a number, the number four, the number five, number 10 whatever that page number is is in the background in the database. That's what it gets stored as but jangling wag tail. Well, they're smart enough to know that Oh, this is a foreign key to a Wag Tail Corp age. So a standard page, basically what this is made out of so I can select any other page. We know that it's a foreign key toe another page, and it creates the object for us. So now all we have to dio is type page dot internal page, and let's ah, let's just grab the title. We can grab the title of the page that we're linking Teoh, which is services. So it should just say services. And sure enough, it just says services. Let's go ahead and grab that. Paige, you were all instead of the title. What's grab that you, Earl? And when we refresh your page, there it is. It gives us the relative. Your l slash services is the page where we could see all of our services, and that does actually match up in the U. R L Bar. The services is the U service listing page and the cleaning page. One we're currently on is our detail page, and that is how we nested it in the Wag Tail. Edmund. So we know that that one works. What about the external page? Well, the external page is just text. It can't be anything else. We told it to just save it in the database as text. That's what a u R L Field is. It's just text. So if we add in another horizontal rule here and we look at page dot external page, let's go ahead. Refresh this, he says. Space x dot com So now we need to determine if one of these has filled Use it if the other one's field use it, and if the other one, or if neither of them rather are used, don't show the button at all. It's now we have some logic to work with. We know that these were going to work. So for this link, we need to say if page dot internal page or page dot external page. If that exists, then the button will show up. We're just wrapping this in an if and an end. If statement. So it's just logic inside of our template. It's a basic if statement now with this girl we need to determine are we going to use or look for, and then use the external page first or the internal page. I like using the internal page first so we can do if page dot internal page if that exists , which we know either internal or external page exists from this. If statement. If that exists, let's add curly braces and you're in dupage dot internal page dot you Earl and that matches this one up here online. 12. Let's go ahead and create an LF Statement elliff page dot external page. Well, type in curly braces page dot external page. We know that's just text, so we don't need do like dot u R L. Because it's not a python object, not a class, so we could do an else statement in here. We don't actually need to. This will never trigger because this will only ever show up if there's an internal page or an external page. But we could do an else statement and just give it an empty link. It's Go ahead, clean that up, refresh our page, and when I hover over this, you can see at the bottom left there, it says Local host Port 8000 slash services. Now let's work with this image a little bit. We have something to do here. Go back to our models. We have a service image. It's also a foreign key, so we know that foreign key is going to be like the number one or the number 90 or the number of 42. It's going to be something along those lines. Both Django and Wag Tail are smart enough to say, Oh, that's what's going to be a number. But I know that's going to map to the wag tail images, app and the image class. So even though it's a number when you try to use it, I'm going to look up the image with an I D. Of whatever that idea is stored. So I D four is going to look up image with an idea for and it gives us that object. So I'm going to make this just eight touch smaller and before we work with images inside of wait a windy to load the images tag. So we do load wag tail images tags, and that just allows us to use this template tag called Image. Now let's go down to where we have this image in here. It's wrapped in a picture, so we can. It doesn't have to be right above it. It could be really anywhere within this content. Block this content blocking here, we're going to say image page dot What is that image called? It's called a service image page out service image. So as its first parameter, we're going to give it the entire image object. Then we're going to say Phil 5 73 by three, 69 and then we're going to assign this as a variable as let's just call it image. I know at this point time we have access to image we could do image that you are. L whatever this u r l is going to be after it's cropped, it will create a new image for us image dot title image dot ault, which is usually the title, I believe, and let's go ahead and refresh us and we can see here. We've got our first image. That's the girl. We've got the name in here and we don't have anything for Ault. I don't believe, actually extend corrected. I believe that's the title. Let's go ahead and test that out. Let's do title. Get rid of that. Yeah, So there we go. We've got image. You are on image cult. So let's go ahead and add an altar in here. Ault is equal to image, not a lt alternative text. And where we have our image source, we're just going to replace that with the image. You are l image dot u r l And there we go. We have a service detail page. Now we don't have a header or footer or anything to actually navigate around the site, but we have a service detail page now. One last thing we can do is we can tell this to use a particular template so we can say, Hey, widow, we know this is a page use a certain template. So let's just bear with me. I'm going to break this, actually, So it's you services slash service page 40404044 because this page does not exist. Save that. And I'm just gonna wait for my server to reload. When I refresh this page, you're going to see that you get a template does not exist error. That's because we're telling it to actually look for services slash service page for a world for overall for four html and that doesn't exist. It's even telling you exactly where to look for it. And it doesn't exist in anywhere that it knows where to look for now I'm going to specify which template to use. Now, this is not going to change anything because this would have been the default. Anyways, this is in the services AP and the page. If you were to take this this class name with a capital s and a capital P and turn it into snake case, it looks like this. And then Django just assumes dot html So this actually is not necessary. But I like specifying a template this way when you're working with code with anybody else and you see a page and you go, Oh, where is that temple? You don't have to look for you just know you can highlight that and you can look for it right in your editor. I just copied and pasted and automatically found it for me, for the problem now is if we go back to our services page. That's if we click this button. Oh, no, service listing page also does not exist. And there are some other stuff we want to do in this page as well, such as automatically show our services. So in the next video, we're going to tackle that. 11. Service Listing Page: all right. At this point in time, you are loading up your website and you're trying to show your service listing page and inner admit that is this page not the cleaning one, the one that just said services services and got a subtitle. And currently there is no template for this. So in this video, let's make that template exists. And then let's go over adding a little bit of context to this. This page is Well, so I have service page opened, and what I'm going to do is just close this actually duplicate this service listing page and, ah, you know, just get rid of this stuff in here, and I've got a quick little template there. Now this page is going to live inside of these services folder inside of the templates a folder, and it's called Service Listing Page because service listing page again. If you take all of those letters, make them lower case. And then the second uppercase letter there used to be lower case. This is what it's looking for. Service listing page dot html and in fact, we can. We can specify that as well, just like we did with our service page. We can come up here and say, Hey, use the service listing page dot html Now when we refresh our page up here, we see absolutely nothing in here. That's because our template has nothing in it. I deleted everything, and it's just showing an empty content block. Okay, so I just pasted in just some HTML structure. You didn't need to watch me type all that out. But the important part is we are going to take this page title and the subtitle and replace it. So let's see what this looks like. Right now we have a page titled We Have a Subtitle and in our listing page it's actually called subtitle, and we know that every way. Tell Paige if I go to this definition here has a field called Title. So what? We can access that as well. So we've got a title in here. We want page dot title, and for our subtitle We Want to Do Curly Brace Curly brace page dot subtitle. Let's go ahead and refresh your page and let's take a look. There we go, says services. Here are the services we can perform on your rocket now. One thing to keep in mind is this subtitle is allowed to be blank. That means when you're editing this page, it does not actually have to be there at all. It's completely optional. And so in our template, we are saying that even if there is nothing in here, still have this paragraph take. So if there is no subtitle, this is what is going to render out to our go to our browser. We don't necessarily want that not just going to be an empty P tag, and that's going to cause some spacing issues. So let's go ahead and throw an if statement in here. If page dot subtitle and let's in dense that just for beauty sake. And if and when we refresh your page, you'll see that nothing really changes. But when we view our source here, if there was no text in here, this entire element would not show up at all. That's all we're doing with that now. The next thing we want to do is add some cards in here, so basically, we want to take if we go back here. So we've got a services page, and underneath this page we have all these different service pages. We've actually got the one. I'll create a few more of these in just a few minutes. What we want to do is take this child page and display it on the service page. It was two ways we can do this. We can do this through the template itself, or we can do it through a page method called Get Context. And that's the way we're going to do it here. We're going to do it. The ultra leg Tell way, All right, so again, I just pasted in some HTML in here. This is just for the most part, it's standard bootstrap with a couple extra styles that I've added. That's all in the front and assets. If you don't have the project assets, you can always download them for this course. They're called rocketman assets dot zip. And so if I go back to my page and I just refresh this, we're going to see standard card. So we've got an image, a card title of description and a link, and what I want to do here is before anything, I really just want to see how this is going to look with multiple items in here. So let's do four I in 1234 And what this is going to do is loop over each number. So this is going to give me and for So this is going to give me four cards and again that just for every eye in string 1234 And when I refresh gives me four cards in here and those are actually all on their own Roe and that is not what I wanted. I actually wanted all of these to be in their own bootstrap row and allow them to wrap themselves how that looks a lot better. There we go. So now we've got four cards in here, so we want to basically do this. So if there's ah cleaning service in here, if there's another service in here to other services, we want to automatically fill these with an image from the service page. That's a service detail Page one we just worked on in the last video with its title, its description and it's Link and naturally, its image. So what we're going to do is we are frank, a second clothes that didn't need that in her service listing page, We are going to overwrite a method called Get Contact so it's def get underscore context. It's gonna take self request all of its regular positional arguments, and it's keyword arguments. And in here, what we need to do is say, Hey, get the regular context of our page. So the context is, Ah, you know where we see due to do to do Let's go back here we see Curly Braces page dot title page, not subtitle. That's all in the context of our template. And so what we're going to do is add a little bit more to it, something that doesn't automatically come just because we have a field name. We want to create some additional stuff, so we're going to use this method, and this context is equal to is going to run a super class super function, rather get context and we're gonna pass in our eggs choir eggs and the request and return context. And what this is going to dio when we refresh your page is absolutely nothing we can see now that it's refreshed. Nothing's changed, but let's go ahead and do a little example here. Let's do context. Hello and this is going to be hello world. And now I'm going to have access to a variable in my template called Hello all capital Letters, cause that's how I spelt it in here. And if I just do this a g l l o. If I save that and go refresh my page Hello world. It shows up as completely custom context, and we can throw anything we want in there. Now I'm gonna get rid of that one, because that's not actually a useful example. It's just an example of how we can basically put anything we want into our context. So is a quick rundown. We basically said, Hey, at page comes with a method called Get Context. We acknowledge that we're going to basically write the exact same function, exact same method we're going to call the old stuff. This is the stuff that comes with Paige. That's what Super does, Super says. Basically, call Paige, don't get context. We're going to pass in all over the arguments that we normally would, and that's going to be the standard context. That's that's what the page will have by default. Then we said, Hey, add a little extra in there. I didn't spell world right, either. That's hilarious on then. Once we've added that in there, just returned the context. And that's how we had a variable to our template. That's the Django way of doing it. And because it's the drink away. It's also the Wag Tail way. So now what we can do is we can say context services is equal to, and then we just have to get all of our service pages now way to get all of our service pages. As we've got a class here called Service Page and Django, we can use their O. R M their object relational manager, service page dot objects, and we could do all and that's going to get all of the pages. But that's has no what we want because our Wang tell pages have a status, they can be a draft. They could be live, they can be public, they could be private, so we want to make sure we're using dot live and dot public, and what this will do is it will grab every single page that's currently live and currently public and throw it in a variable that we can loop over called services. So let's do this. Let's get rid of this four loop in here. Well, not really Get rid of it. Let's just do this four I in services and we will step by step will incrementally make this actually work. Now when I refresh the page because I only have one service page in here, this is the service page here. Just because I've only got the one. This will only show up with one card just like that. Now what happens if I duplicate this page? That's copy this page and let's give it a shining. That's what this new service page is going to be. Do I need to change anything else in here? Let's go ahead and let's change his image and I'm going to upload a new one, okay? And let's just publish that page. Now, when I refresh this page, I'm going to have two cards in here. The reason I have to is because there are two of these pages. And if I said hey, actually, let's unpublished the cleaning one. Yes, I want to unpublished the cleaning one. Now. Wait Till says, Yeah, okay. I understand that there are, in fact, some of these pages. But only one of them is technically live, so only show one of them. And that's why we see the one card here. Now I'm just going to republish it's page, so we have both cards. And now let's go ahead and change each piece of this. So in our editor, I'm going to say, for each card in service ISMs in a call it card. The simplest thing I can change here is the title card dot title, and if we go back to our models in here Models, models, models, we're grabbing all of the service pages. So we now have access to every single field that's available in a service page. We now have access to the description, so let's grab this description as well and replace this one card dot description. Now where am I getting card from? Well, because services is, it's a list of basically the service page. We're going to loop through each one card because we're going to create cards on the front end. And so, for each one of these pages, one of these cards we have access to the title and the description just like a regular page . So let's go ahead and save this and give us a refresh and we can see that we've got cleaning shining. We've got some alarm ipsum in there, and now we need to change the URL and the image. All right, so we let's let's do the image first. I'm just going to be the biggest one is going to the funnest one. We need to make sure we have wag tail images, tags loaded, which we currently dio, and let's go ahead and create a new image in here. We're going to say image card. And then what is that field called? It's called Service Image. Going to give it a service image, we're going to say Fill it with 5 73 by 369 as card image. And then, just like in our last video, we can use card image dot u R l. We can also give it an altar, which is always a good idea, even if it's empty. Guard image dot dot a lt for the word cult. And look at that. This is starting to come together now when we click this, this isn't actually going to go anywhere and you can see at the bottom left of my screen. It says at to do So let's let's fix that. Now we know that every card has a page. Well, because that's all this is this is a page object. We just renamed it to card for this loop. So we're going to do for each card, generate a card. You are l We're going to put the girl in here as well, and we're also going to put it in here. So basically, when anyone clicks anywhere in this card text image or the link is going to bring them to the proper page. So here we go. I refresh the page. I click Cleaning brings me to the cleaning page. I click back, Click Shining brings me to the shining page and there we go. Just like that, we have a services listing page, and for every new page that you add, it will automatically show up in here. It will just keep adding them. That was a super quick summary. Here's what we did in this video. We added the page title page subtitle We check to see if the page subtitle even exists. Actually, there's one other thing we should dio is. Is there a description? Eso. Then we said, Hey, we're going to add some custom stuff into the context of our template. So then we added, I get context method. We said, Hey, grab the regular stuff that wait till always give us and then add some extra stuff in your This is using Django Zo R M So we're grabbing all the service pages were making sure that all the pages air live and public, and we're simply returning them to the context. Then, in our template, where the context is being injected into, we can loop through every service. We called it card. So for every iteration we call it card, we created an image, give it a title or displayed the title, displayed the description and gave it a your l. It was one last thing I did not do is I forgot that the service page description is, in fact, optional. So we've got service page description. Blank is equal to true. It is an optional field, so we're going to say if card that description and simply end if now it's not going to change the display because they have a description. But if there wasn't one, there's not going to be an empty paragraph tag and her document object model in her page in her html. And that is just one of the ways that we can create a listing page and list all the child pages underneath it, just like we've done here. There are a few other ways that we can do it, but this is by far the simplest. 12. Adding Page Validation: up into this point, we have been adding a couple of fields into our service pages called External Page and Internal Page. And what we're basically saying on the template is, if there's an internal page, use that you, earl, or if there's an external page, use this text and now is a good time to add some page validation. So if both of these air filled out, we're going to add a little error that says, Hey, only one of these can be filled out at any given time. So I'm going to open up my editor and in my service models dot pie file. And if I closes up, we can actually see where this is services and then models dot pie. We're going to add another method in here called Clean. We're gonna say, Define clean that's going to take self super clean, and that's just going to do its regular cleaning. So whenever you add content to the page just going to escape your content so we can, you know safely be entered into your database and you don't have to worry about SQL injections and things like that. Now what we're going to do here is say, if something cause and error, that's really all we're doing at this. So if you just keep this in mind these two lines in mind, this is actually quite easy. So we can say if Self dot internal page and self dot external page both fields are filled out, and that's actually know what we want. We don't want both fields to be filled out. We want to just one of these to be filled out, and this helps guide your content entry people to doing basically the right things. I don't fill it out in both places because that could be confusing. And if anyone else were to come onto your page and say, Hey, there's a link here, nothing here which which one's going to be used? I I don't know, but this is going to do is throw you an error and say, Hey, only one of these could be used at any given time. So now let's go ahead and add an error So we're going to raise an exceptional raise and error here, so this is pretty much all jangle at this point. We're going to raise a validation error and we're going to give this a dictionary, and the 1st 1 is going to be whatever your field name is. So what this is saying is, if there is, in fact an internal page and an external page, that's this if statement here, we want to throw an air onto one of those or both of them. So I'm gonna throw on air on the internal page field first. So that's going to be the key for the first dictionary value here. And then again, I'm going to give. It's just one more validation area. It seems a little weird that we're nesting that. But that's all we're doing here. And we can say at this point please only select a page four, Enter an external, you are l. So now that's all fine and dandy. But if you actually go to save this page, you're gonna run into a problem. We need to import validation air. We have not used this yet, and so we have not imported it yet. So let's go back to the top of our page here. Now let's do from Django decor dot Exceptions, import validation. Error. That's all we're doing. I'm gonna slide back down here clean that up a little bit. Save Django decor dot Exception does not exist. That's because that was a tape or lack of typo. It's Django. D'accord out exceptions. There we go. Now let's go ahead and try to save this page. What happens when I even just try to save a draft? This page could not be saved due to validation errors that's looking good. It's looking good. And here's the exact text that I wrote. Please only select a page or enter an external euro. No, there's no error here, but we can add one very, very easily. So let's go ahead. Scroll back down here and I'll make this just a little smaller. Size fits on one line That's far too small, and I'm isn't a copy and paste this because all we need to dio is changes field name, and I suppose we should have a comma in there because this is a dictionary. So this is the first key with the first air message, and this is the second key with the second error message, and this text, by no means need to needs to be the exact same. So let's just do that that that that that that that the dead I'm gonna save that and you'll see that the first air is gonna say something very polite. And the second error is going to have a bunch of disease. There's ads in it and is going to say the exact same text afterwards. Let's go ahead and save draft. The page cannot be saved. You do validation airs and please select a page or enter an external you well, and in the 2nd 1 down here, we've got said that that that that that that that dead please only select a page or enter an external you Earl. So now we've got two errors for basic validation. So actually gonna clean that up because I don't like all those heads in there. Doesn't look very nice. He even throw this into a variable to really clean that up. And now, at this point, all we have to do is, let's say, get let's get rid of Space X. We don't want to necessarily linked to Space X. On which page are we on the cleaning page? So let's go ahead and let's just publish this now and it works. There's no more validation errors. It actually allows us to save the page, which is really, really good news, because up into this point we can add an internal and an external you Earl. Now let's say every page needs to have one or the other says Go ahead, clear this choice. There's no internal page and there's no external page and it saved perfectly fine. What we could say now is, I'll just make that slightly bigger again, if not self died internal page. So if there's no internal page and not self external page, so there's no internal page and there's no external page, we can also raise an air in here. So let's go ahead and raise a validation error. This is going to be a dictionary. And again, it just looks like this. We've got a key with validation error and then a second key with another validation air. That's really all this. Looks like we want to put those keys as the internal and external pages and the validation air is going to be. You must always select a page or enter an external you Earl and I'm isn't a copy that cause I like that wording and That's good enough for both those fields that I'm working with. So I'm gonna save that. And now when we save our pages looking for two things, if both of these air filled, then it's going to cause an error. It's gonna throw in air some validation air. And if neither of these air filled out, it's also going to throw validation air. So let's go ahead and saver draft. Hey, look at that. The page could not be saved due to validation airs. You must always select a page or enter an external You Earl says the same thing down here. Texas Nice and read your text field itself is nice and red, so people content entry people know exactly what went wrong. Now I'm going to choose any page. Ah, and I am in fact going to choose the home page published, and it publishes perfectly fine and so ultimately, this is all we do to add a validation error for our page fields. So we've got a page up here called Service Page, and we want to make sure that there's always an internal or an external page set, and there's only ever one of them being set at any given time. 13. Adding a Header and Footer: if we take a look at our page right now, it looks empty. We've gotta services, Page, We've got service detail pages and we've got a home page, but we have no way to actually get around. The home page has a nice banner. That's cool, but there's no header or footer. So how are we going to get around? Well, let's go ahead and add a global header and a global footer to our Wag Tail website. So first things first. I'm gonna close that and open up base dot html and that lives inside of our Rocket Man folder and inside of the sub folder called Templates. Now in here, I am simply going to say, include a file includes header dot html and that's all I'm going to dio and below all of my content, I'm simply going to include a footer dot html. Now this is going to look for template files. This is a template file. We're working with the base one, and it's going to look inside of our template folder that's right here for another folder called Includes and then a file called Header. And then it's also going to look inside the includes folder once more for a file called Footer Now if we save us and we refresh our page, yeah, template does not exist. We've seen this before, and this actually tells you it's looking for a template. So in the templates folder or in one of these template folders for a some folder called Includes and a file called Header dot html. So let's go ahead. Creating new filing here includes header dot html. So I just created a folder and a file, and I'm also going to go ahead and create footer dot html and these were totally empty files. Nothing in there. So at this point, we have effectively done nothing to our website. So if we open up header dot html, we can had basically anything. So let's do this. Div. This is ah, header Cool. That's gonna show up on every single page. So if I go to slash services, this is a header. Shows up there, go to a service detail page, it still shows up. It's always there, So let's make this actually look nice. Let's make this actually look like a proper navigation. All right? So all I did here was I basically just took a navigation from get bootstrap dot com and I injected it in here. I did one little thing. I added one link. It's not even a real link, and I gave us a relative linked to our logo. That's it. So when I go and refresh your page, we're going to see we've actually got Rocketman logo and a single link, and that's going to show up again on every single page. So as I hit the back button, refresh that one. There we go. I'm on the services page. I can go to any of the detail pages, and that header is always there. Now there's two things we want to do here. First, we want to not link directly to Static. You might see this in other wait till projects you might even see this. Another Django projects where people just link directly to the static folder, and for a lot of purposes, that's actually OK. But for some websites, you might not want your static folder to be called static. You might want it to be something different. I don't know why you would change it, but some people just want it changed so What we're going to do is use the static template tag static, and we're going to simply give this a string and say, Hey, wherever static is whether we change that now or if we change it in the future, wherever that folder is jangles gonna figure out where that is the images sub folder inside the static folder again, we don't know where that is, so it's very relative at this point and the file logo dot PNG Now, to make this work, we actually have a load this in here. So we're going to load the static tag. And when we refresh your page, you see nothing changes. And even if we look at the source in here, it looks the exact same slash static slash images slash logo dot PNG Looks like nothing changed, but in production, for example, if you gotta wait till dot io and we right click and inspect here, you can say yes. This is using static as well. They could have used any other folder. They've got a sub folder called Image, but then they've got fingerprinting turned on. So every time they collect static every time they had more assets to the website. You're going to get a random hash and effectively what that does. Is it cash busts, all of your assets, all of your CSS, all of you, JavaScript, all of your images. So that's if you're using Amazon s three or cloudfront or digital ocean spaces or anything that might have cashing it immediately gets busted. And that is why typically, we like using static. So it's got to benefits, Really? The other thing I did was I added a Lincoln here. So if I go back to our page, I just have one link, and what I'm going to do here is I'm actually gonna do another loop. So for I in 123 I just want three of these items. And so I just did some in denting and added the end of four tag in there. Let's go ahead. Ad I as well. This is going to be our menu in the future. So for every eye, for every integer in 123 show link one link to link three. And just like that, we've got Link one linked to link three. Please don't go anywhere. We could hard code these if we wanted to, but we don't actually know if our future client or future Selves are going to maintain those links. So, for instance, I could say Link one always goes to slash services. But what if one day I go to my services page and say, I don't want it to be called services? I wanted to be called rocket maintenance services. And maybe that's just better for S e o. Well, all of a sudden, my link that goes to slash services, this page is going to break, so we don't want to do that either. I'm not actually going to say that. I'm gonna keep that add services and that's how we add a header. And next, I'm going to add a footer. So if we go back to our base, we've got includes Footer. But our footer page currently has nothing in it. Okay, so again, I just pasted Ah, template, footer in here. It's a bunch of hte email and whatnot. There's actually nothing really going on here, except there's a for loop toe create fake links again, just like we did in the header and ah yeah, that's about it. So let's go ahead and take a look at this just as a quick preview. So I'm gonna get rid of that scroll on down, and I just refreshed my page and there we go. We've got a global see ta Uh, some text in there, a button that's gonna eventually go somewhere. I guess social media icons hours to do is links 12 and three. And so these are all features that we're going to be adding in a little bit later. But for right now, it's just nice to know that we are starting to actually create a website that looks like something actually looks like a website. At least I can get to the home page from really any page at this point and in future lessons, we're going to add a proper header in here. Ah, proper navigation. So we're going to create menu items. We're gonna create three of them in there. We're going to then copy, though, so they also loop through the links and create the exact same links down here. We're going to add global website settings for the contact us for the hours for social media settings for the links. We're going to make sure this is customizable. This is customizable, invisible. And this page this to do button actually links to a proper page. So we're going to get to all of that down the road. But for now, we're just going to keep it here as placeholder. 14. Adding Debug Tools: okay, Our website is starting to get complex enough that we should probably start considering some debugging tools, something that's going to help us along the way to figure things out. Figure out if there's a problem which we haven't actually really run into anything, but it's gonna help us along the way. So we're going to install two things. The 1st 1 we're going to install is going to be create new file here is going to be called Django Debug Tool are, and the 2nd 1 is going to be called P You d Be. And that is a python interactive de bugger. So it's a little better than the regular D bugger. It's a tool. I like both of these air totally optional. If you have better debugging tools that you prefer to use, definitely use those. But these air to that. I absolutely love that. I pretty much put on every single project just because it helps me out tremendously. So let's go ahead, open up our terminal and cancel our server. And while we're inside of our environment, so whichever virtual environment you're using, whether it's Pippen ven vor v envy docker, vagrant, anything like that. Just make sure you're inside of your environment and type Pip. Install Django Debug toolbar and you can see that successfully installed Jingo Debug toolbar. Version two point once lets you pip show Django debug to a bar, and that's going to give us the actual versions. We've got a name here. That's our name. And we also have a version which is 2.1 so cool We have that installed in a virtual environment. Now the first thing we want to do is we want to open up requirements dot txt Sure, whatever is in there, let's go ahead and create a new file offer this one called dev dot txt and deleted. The reason we did that is because I want dev dot txt and requirements dot txt toe live beside each other in our project directory so you can see I've got requirements out txt in the same folder. I've got dev dot txt Now what we're going to say inner dev dot txt is require the requirements dot txt file and below it, we can put Django debug Toolbar is equal to equal to version 2.1. So now when you spend up a new project. Instead of doing pip install, dash our requirements like what we usually do for a new project. You can do Pip Install Dash are dev dot txt and it will install of your base requirements, but also the Dev requirements, such as debug to a bar or P. You d be No, but installs it into our virtual environment. But that's not actually installed into our project. So let's go ahead and close that and that and let's open up dev dot pie. So these are our development settings. We can add our own development settings and here, or we can overwrite them with whatever local settings we want in a local dot pie file. I'm just going to throw them in here because when I commit this, I want other people to have these exact same changes. Often we don't really commit local DUP high files or local files at all just because it's a little extra tweaking that you need to do to get the project running on your computer. Whereas I want everybody to have the so I'm gonna put this in the dev dot pie file now. First thing I want to do is I want to add this to my installed APs. Let's do installed APS and this is coming from base dot pie. So if we open up based on pie online 26 all the way down to Line 52 we have our installed APs. So we're basically saying, Hey, Django, use the base settings That's cool. And also grab your installed APs and add some Maurin there. So let's go ahead and do Plus is equal. And let's throw a list of items in here, and this one list is going to be debug toolbar. And at this point time, we can rerun her server if we want to see if anything comes up any sort of errors. Look at that. And it says, Hey, Django, debug toolbar Middleware is missing. We should add that to our middleware. Okay, let's go ahead and add that to her middle where now all of these could be found on the jangle debug toolbar. Read me instructions. So if you go to, I think it's ah, read the docks or Django debug toolbar dot Read the docks. A quick Google will will tell you exactly where that is. It'll tell you exactly how to install this. This is really just I'm doing most of this from memory here, So let's do middleware. And again, let's add to our middleware, and we're going to give this a list of that debug toolbar middleware. What was that complaining about this one right in here. We want to select all of that and paste it in there. That's what is complaining about, and lastly, this is a weird one. But Django Debug Toolbar requires us to give some sort of internal I p that is allowed to use. And for this we just want to use 127.0 dot 0.1. That is your local host Save. Let's go check out our terminal. Everything looks OK in terminal land. And let's refresh our page here and at this point time you actually can't tell, but it's trying to work. We have one more step to do here, but it's really trying to work. So generally we're going to see a little Django icon up here, and that's going to be our profiling tool. So the last thing we need to do is open up our you or else dot pie and that just lives under Rocket Man. Here we've got a Rocket Man folder, right beside setting static and templates. We've got a file called you URLs dot pie and in here we we want to basically add some extra you RL's so that the jangle debug toolbar is allowed to show you what it wants to show you . So let's go in here due to do to do settings. We're going to say, if settings debug is true, that means we are developing locally. Debug is only ever true when you develop locally. Once you go into production, you should have debunked earned. Often you shove Engine X or some other sort of application serving your static files. But for now, this is okay. Let's import debug toolbar, and we don't need to do anything with these. And let's just go ahead and add debug toolbar to rul patterns. So again, this is only going to show up for our local development. So you are all patterns, is equal to your old patterns, and let's let's upend it a different way. Let's add Uriel patterns at the end. You are L patterns is equal to whatever the list is plus existing neural patterns. But that path underscore. Underscore debug underscore. Underscore slash. And then let's do include debug to a bar that you or else now that's going to break because we probably don't have path or include installed or included the right way. It's probably installed to come to the Django, but it's probably just not imported the way we're expecting it to be imported. So let's go back up to the top. Do we have Django you or else we've got confident you are else, but we want from django dot urals import Include in past. Okay, let's go back and refresh our page and it's still not showing up, so I obviously did something wrong. Go back to dev dot pie installed APS middleware installed. I p is No, that was supposed to be install. Nope. Internal eye peas. Let's go ahead and give that a refresh and has ah, there it is. So it Ah, I guess a little finicky. I mean, there's a few steps, but again, that's all documented either in this video or in the jungle toolbar Read the Ducks website . So in here we've got jangle debug toolbar. It tells us the version that we're using the processing time shows us or settings. How many SQL queries. This is a big one because you're working with a content management system. You can end up bloating this to be a lot, ideally, shoot for less than 100 queries if you can. If you ever see a website that has 1000 queries, something definitely needs to change. That is, that's terrible. How many static files are we using and actually go back to the SQL queries? We can see which ones have been run in here. That's pretty cool Weekend Say, Hey, explain yourself and it does. It tells us what's going on. That's pretty neat so we can see our static files. We can also see our templates in here. We've rendered 13 templates, so we've better service listing page. We know where that's coming from. This is really helpful. When you started an existing project and you're like, Oh, where is this template? Where are these templates coming from? Don't know where they're coming from. This will tell you exactly what's coming from. This comes from services slash service listing page. That's pretty cool. We know it's using based on HTML. It's got a bunch of Wag Tail stuff in here. So it's using the user bar more weight telling men stuff, and we've got an include for Header and include For Our Footer was telling us all sorts of things, even tells us what's going on with cashing and signals and logging and all that good stuff . So this is a full profiling tool. The biggest helpers in here are your SQL queries and your templates. If you have a site that's loading really, really slow or you don't know where a template is coming from or where some data is coming from in a particular template, this is going to help you out tremendously. So this is to a number one tool. Number two was called P You d be. So let's go back here. This one is actually super easy to install, so we do Pip install P you d be. Let's go ahead and do pip show p you d Be and it's using 2019 dot to let's go ahead and restart our server with Python Men Shop. I run server 0.0 dot 0.0 cool and 8000 and we could close up all of those. And let's open up dev dot txt again. And let's put P D. B is equal to equal to 2019 dot too. So we're version locking the version of PDB that we're using in our project in just the Dev requirements. And for this one, that is literally all we need to do. So let's give this one a shot. Let's go to service models and to activate this one, this is super easy. You just throw it wherever you are, having some troubles when you need. You need to debug something and you just type import you d be colon and then p u 0.2 db braces. And that will run your d bugger for you. So what? This is going to you? Is this going to tell me everything that I have access to in this particular view? All the variables, all the data, everything. Let's go ahead and create a new variable in here. Just as an example, it's called Space Company is equal to X. Make sure our servers running Okay, looks a okay. Now let's go back to our services page and let's just let's refresh this page. The reason why we're on this page, by the way, is because we have a service listing page and we're adding custom contact. So we've got some custom logic in here, not really logic, but some custom data that we want to throw into the template. And maybe we need to check this out and figure out what's going on. So among the service listing page and a hit refresh, and it looks like it's just going to load forever. But if you boot up your terminal again, you're going to see this extremely ugly page now. Usually there's a little pop up in the wind in the middle there, you just have to hit. I believe it's the right arrow and then enter, and that will just get you in here. And then you can hit left and right, and you can go through here. I usually just go straight to the right section. You can see the green stuff is changing in there, and so we've got access to the guards. The context, the keyword arguments, who had access to the whole PP. You d be thing we don't need that were already running. It request is running a whiskey request. Self is the service listing page. Our custom variable called Space Company, is equal to X, and that's it. We've got some other stuff in there that we're not going to get into because this is the biggest help. So let's go into our context. I'm going to hit the slash key. That's right above my return key, and that's just going to open it against not super intuitive, but it is super helpful. And in the context we have page self request and services so we can actually see that when we go back in here to service Listing page, we've used page page dot something page dot title paid shots. Subtitle And so if I open up this one again using that same key, we can see everything that's available to us in the template. And ever since, scroll down until I see the word title dot tono, Where are you? A CEO title slug all sorts of stuff. There's our subtitle here. All the services we can perform on your rocket. That's exactly right. And this is the page title is just called services. This is our page. You are l We've used that one before, and so now we can actually see everything that's in here. We've got a full ul path. Should you ever need that all sorts of stuff, self is going to be the exact same thing. Self and Paige are identical, but the one we added, maybe the one we want to debug is these services. And that's because if we go back here, we said Context services is equal to service paid shot objects that live that public. Maybe we don't know everything that's in there. We can't figure something out. It just seems like it's throwing some mystery data at us so we can go in here. We can open this one and it says zero and one, so we automatically know it's a list. It's not a dictionary. Dictionaries have named Kees. Lists have indexes or indices just for their index zero and one makes it a list. So the first page is going to show up, and this is the service page who could actually see appear. So zero that's the 1st 1 is a service page, and then we could use scroll all the way down, gets all sorts of things Paige. I d. Number five. That's neat. Is it life? Yep, that one's live. Is it lock? Nope. It's not locked, Max Count. We haven't learned about that, but we will, Oh, sorts of stuff in year and there does title. The 1st 1 is cleaning. Someone keeps going down and go to Number two your index number one. But it's the 2nd 1 in our list. Just go all the way down to where it says dot title again shining. So the first services cleaning the second service is shining, so that's telling us exactly what we want. Or in this case, I want to see just the titles, which would be cleaning and shining in that order, cleaning and shining. So that's really cool. That's another way of really just debugging what is being presented to you in a particular contact. So for us, we're in the get contacts. This is where it's being run right in here, And this in the top right is all the data that we have access to. So if you're ever wondering, what is that? A CEO title field name? You just go in here, look at the CEO title name or you could look at the wait till source code and then, lastly, to get out of here, you just hit the letter Q to quit. It will complain. Yep. It will complain. No exception. Message supplied. That's okay. And we're just going to get rid of that. Don't that one either. Scared of that server restarted itself, but if it doesn't, you can always restart it manually. Refresher Pidge. And we saw over debug toolbar and everything. So there we go. We have Django debug toolbar and P you d be installed. We've also added a new file called dev dot txt. So whenever you install this project, you're going to type pip install Dash are dev dot t x team and it will install everything else from the requirements, not txt file, but also Django debug toolbar and P you d be. I'm hoping this is gonna help you out in the future again if you have your own tools and you don't like these ones, totally disregard this. But these two tools have helped me tremendously over the years and even today I still installed him on almost every single project just so that I can have that little extra helping hand 15. Flex/Misc Page: All right, let's go ahead and create another page type. Now, this is not the most exciting thing that we've been doing so far. But we're going to be creating a regular page type. I'm gonna call it a flex page. Ah, some people call it a generic page or basic page. Some people call the miscellaneous page or a standard page, but really, the idea is that we have some sort of page. It doesn't have any real purpose except to serve content. So this page is not going to necessarily be related to ah, blawg or services. It's not like a service detail page where it actually has a particular purpose. This is just a regular content page. No, we're not going to be adding content just yet, but we do need to create this page for a future Lessons. Let's go ahead, activate your virtual environment and go ahead and run. Python managed up. I start app. I'm going to call it Flex, and then go ahead and run your server with Python managed up, I run server Port 8000 and in our code, I'm going to open up base dot pie and right underneath services I have a new app in here called Flex and you can actually see in my editor here. I've got a new folder called Flex, so I'm gonna save that and open up Flex. And I'm gonna get rid of some stuff that I know we don't need because Wag Tail takes care of a lot of this stuff for us. So views up. I we don't need We're not writing tests. We're not doing anything with our admin. We do want to keep migrations in it. APs and models, that pie and really all we're going to do is create a regular page once again. So we're going to do from wait till dot core dot models import page, and then we're going to create a new page called a Flex Page, and it's just going to inherit the page model. And for now, we can do pass, and that would be good enough to create a page. Now let's actually take this one step further. Let's do class metta, and let's give this a ver boast names. So we're going to see that we can actually change the name here, so that's due for both Name is equal to flex, and it would be normally just called Flex Page. But we're going to call it Flex Miscellaneous page, and the verbose named plural is going to be flex missing pages just like that. Now, if we open up a browser and I'm just editing the home page here, if I go to Add Child Pidge, we have a flex misc page in here. I'll just make that a tad bigger now when I click this because I haven't run migrations, I expect this to throw me the Classic 404 We ran into this air once already, and that's very much expected behaviour. So let's go back to our terminal. Cancel that, and I'm just going to clear that page off. And now I'm going to run python manage dot pie, make migrations. And then, once more python managed up, I migrate and again, what that does is when you make migrations like this one just created a file called 0001 underscore initial dot pie. And that really just writes out python IQ instructions for what Django should do to start shaping your database so that this page actually works for us and then when we run Python managed up high migrate. This will apply this new migration file, and it will actually structure to page that we're looking Teoh to create in our database. So it manages our database table on columns for us. Now let's go ahead, rerun our server, close that down and refresh. And in a sec, this will load. We can now create a new flex page. So let's just call this. I don't know about us. Just a generic page Now there's nothing else on this page it We're going to keep it that way just for a little bit. We just need this page to be up and running. So go ahead, publish this page. Things were looking well, when we click of you live. We've seen this before. We've seen this a number of times. We're going to get a template, does not exist error. And just like that template does not exist at about us. It's looking for flex slash flex underscore paid shot. HTML. We can actually go and change exactly which pages looking for. We know that if we look in here using engine Django, that right sort of in this right hand air area. We can see that it's looking for Flex Page in the Flex folder, which lives in the Templates folder. It's also looking for it in the home app and the search app and then also inside of all of our packages. Let's go ahead, make this template actually exist. Open up our code and I'll just close all of that open up, Rocket Man, go into templates and let's create a new folder in here called Flex and then a new page called Flex page dot html. And this is exactly where Wag tails looking for this template. Next, we need to actually extend this from a page. Let's extend this from based out hte email. That's where all of our HTML is going to originally come from, and we're just going to overwrite a little bit in here. Let's create a new block, and we're gonna call it content. Now if you're just joining us, if you open up based on HTML, we have a blocking here called banner content and extra Js. We're going to take this content one, and we're just going to inject a bunch of HTML right in here. So what? I'm going to do is just right. Some bootstrap code real quick. So I've got a row. I've got column medium eight, and let's offset that by two So offset MD to and I have an H one in here. I've got a custom class that I'm going to be using called CC inner title, and in here we're just going to use the page dot title page dot title and every way tell Paige comes with a field called Title, so we know we have access to that. Let's go ahead and refresh our ever page and we can see there it is. We've got a little about us title. That's exactly what I inputted into. Wag Tail is a content management system that's the title of our page. No title of our page shows up here, so that's all there is to really creating a brand new app and a page from scratch again. We're going to be adding a little bit more to this page as we start getting into stream fields a little down the road so this page could be completely customizable. So go ahead, get commit that save it, do whatever you need to do, and when you're ready, we'll head on over to that next video 16. Testimonial Objects: Up until now, we've been creating Wag Tail pages with just a few custom fields. This has largely all been just weg tale stuff. But now let's take a step sort of backwards or outwards rather. And let's work with Django. And let's enable a jingle model to be accessible inside of the wag. Tell admin. Now, basically, with the Wag Tail model Inman, we're not going to be needing the Django model Inman. Now, if your brand new d jango whenever you work with models and you create new data, it comes with the Django admin, and you can manage all of your data that way. Well, when you create a way till happy you now have to Edmunds and we're going to basically say, Don't use the Jan Goodman at all, I haven't even shown you the jangle wedemen. We're not going to take a look at it. We're simply going to use Wag tells model Edmund. So I am in my virtual environment and I had my server running, so I'm gonna cancel that and I'm going to create a brand new app. So I'm going to do python manage dot pie and we're going to create a new app called testimonials. If I could spell the right to test it mony als and then I'm going to run that server again and open up my code. Close the stuff from the last lesson and you can see in here I have a new folder called Testimonials. Now, views are really just a jangle thing. You don't need it too much when you're dealing with wag tell unless you're writing a custom jangle view. So we're gonna get rid of this file. Well, we will be writing any tests. I don't think we need to write a test for this. And we're actually going to keep our ad men APS models DuPuy In It Up High and Migrations Folder. So the biggest change here is that we're really just keeping the admin dupuy file. Let's go ahead and open models dot pie. And before we do that, let's go ahead and open base dot pie And let's actually activate this. So we have all these different APS. We have a new app in here called testimonials that matches the folder name. Where did you go? Testimonials. Right here. No, we're going to dio is save that and close Based on pie? No, We created a jingle app, and this is pretty much all we need at this point. We're going to say class and we're going to add a testimonial, and it's going to inherit models dot model. This is just straight jangle. This at this point has nothing to do with Wag Tail. And we're gonna call this a testimonial class definitely needs a better doc String. But for now, that's all we're gonna put in there now. What is a testimonial? Testimonials A quote and has a person. So we're gonna say attribution So what is that quote going to be now? This is going to look a lot like a regular way tail field. So if we go to home models dot pie, we've got lead Texas equal to a char field. We've got foreign keys. Notice how is using models that pie here? We're basically going to be doing the exact same thing. Only this time it's not a way. Tell Paige it is a jangle model. So a quote. How long do we want her quote to be? Let's give us a text field with a max length of something relatively short. 500 characters. Can this be blank? No. Can this be? No. No, this is a mandatory field and attribution were basically going to do the same thing here, but we're going to use a char field, so this one's going to have a max length of se. Ah, maybe this needs to have a name and a business name. So Caleb Tallinn from coding for everybody. So I'm just gonna guess that's roughly 50 characters. I Can this be blank? No. Can this be? No. No. And I am going to split thes onto their own lines because it's just gonna make life a little easier if we can see this all on different lines. There we go. Now we can do a few different things here. We can optionally give this a verbose name. We did this in the last lesson for boast. Name is equal to its a testimonial testimonial and the verbose named plural could be testimonials. Now, jangles already going to do this for us. Wag Tail already does this for us. We don't need to set this because, well, it's gonna take our class name, testimonial and plural to plural eyes. Something in English Basically you add Justin s And so it's saying, OK, I'll just add an s to whatever the for most name is so technically this is optional. It's not required now. If you wanted to change the name, but you wanted to keep the model name as testimonial, you could do that. This is for display purposes. Next, we want to give it a string. So death underscore. Underscore string underscore. Underscore inner str It takes one parameter self because it is in an object. It's in a class inside of Python, so it takes self. And this is just going to be the string representation of this item. And I'll show you what that looks like in a little bit the string representation of this class for this object and we're just going to return itself dot quote or attribution Let's do self don't quote. In fact, let's not do yourself a quote. Let's return an F string. So self dot quote by self dot attribution So it's going have a very long string name. Probably too long to be totally honest, but that's what we're going to work with now. Let's go back to our server. Cancel it and let's run Python, manage that pie make migrations and all this is going to do is create that classic 001 initial dot pie file. We've seen this before. That's basically just our instructions written in python on how jangles going to structure our database for us. Now we're going to actually apply those with Python managed up high migrate that works. Let's go ahead, rerun her server and let's go back to our admin. So I'm just back in my wag telling men here, and I'm just gonna load up a page and you're going to see once it loads, nothing has changed. You're not going to be able to find testimonials anywhere in here. The fact is, it does exist. We have structured our database with testimonials or with a testimonial table, which we can see in our model here. However, we don't have access to this at all. So let's go ahead and give us access to our own piece of data here so that we can add testimonials. So let's go ahead. Open up at Minda Pie. This is the first time we've opened up at min dot pie and I am actually going to get rid of all of this. This is Django Wittman, and we're going to be using Wag Tails Admin. So let's do, Ah, little import here. So let's do from Wag Tail Dodge Con Trib dot model admin dot options import. And we want to import Model Edmund and also model admin Register. Now, before we continue, you can see that this is coming from the package called Con Trib. I'm not really a package. It's more of, ah, holder for contributions for additional features that Wag Tail can come with. We need to make sure that this is enabled. So let's open up our base dot pie and let's look down here for wag tail dot con Trib dot model admin. So we do model Edmon. I don't see it anywhere in here. So we're going to want to enable this, and I'm just going to put it with the rest of the contribute Weddell dot con Trib dot model Edmund, Save that. Let's check her terminal. Everything seems to be a okay up here. Go back to our browser. Give that a refresh. Life is going to look good once I loads. There we go again. Was not going to see anything. So it's closed based on pie. Let's continue registering our application here. Next, we need to actually import the testimony itself. So because this isn't the same folder at min dot pie and models top high, we can just do from dot models import testimonial. Thank you. V s code for auto feeling that for me. Next, let's go ahead and actually register this model. Inman. Now what we need to do here is we need to create a model it men with our testimonial. That's this class here. And then we actually need to register it. So we're going to create a class a new class and admit in class and then register that. So let's call this testimonial. Admin could be called really anything and is going to inherit model admit nuts. That one right there. Thank you. Have that doctoring of testimonial at men That is a time tested and pointless doctoring. But you know doctrines, doc. String So model is equal to this is where we start to actually set our model women. So model is equal to test him memorial, and that needs to match the actual testimonial class. That's the Django model that we see in models dot pie. That's this one here. Next, let's do menu label. What do we want to call this in our wag Telemann. So when it shows up in her menu over here, what do we want to call it? I'm gonna call it testimonials. Let's give it a menu icon as well, and I'm just gonna give it a placeholder. A menu order somewhere between 101,000 issue. Let's, I don't know. Let's give it a 290. Add two settings menu and you can find all of these settings in the documentation as well at docks that wait till dot io. I don't want to add this to the Settings menu. What this is saying is that it there, Let's not added there, we don't need it there. Exclude from Explorer is equal to false. Let's not excluded from the Explorer, and there's two more in here list display, and this one's going to be equal to you. Something we're not gonna add that just yet. On this do search fields is equal to something as well. We're not gonna add that either. Let's go ahead and save this. Check our terminal. Make sure that it did actually refresh. Occasionally it doesn't. And let's go back to our wag tail page and just hit Refresh. And sure enough, you see that it still doesn't work Well, that's because just a minute ago, a couple minutes ago, I was saying that we need to create an admit model and then we need to register it. So let's take this model had been register and let's add this as a decorator. So we're gonna put that there hit save again. Make sure Terminal are Django Server restarts itself. Now let's hit refreshing. We'll see on the side here. It's going to say testimonials just like that with our little icon. That's a placeholder icon. We specified that one testimonials. We don't have a testimonial. Let's go ahead and add one. What to do or do not. There is no try classic Yoda to go ahead and save, and we have a testimonial. We can add another one in here, and this is going to be oh, from Chewbacca, and they're really so we've got testimonials in your That's pretty cool. Now, at any point in time, inner get context functions when we're when we're editing a page like I was going to reference it. But let's actually talk about it. We have a Service Models page here, The service listing page and Inter get context. We can now actually say testimonials dot objects dot all, and we can get all the testimonials. So that allows us as developers, to allow content entry people or your client to input some form of data. And we can then use it a little bit later. All right, let's uncommon that list display, and we want to set two fields here. So this is going to be how it's displayed right now. It's just the one column just this testimonial and this is the string representation. So that string that str method here says the quote by whoever said it. So if we did this a bunch of junk in here save that, wait for server to refresh, we're going to see all that junk actually shows up in her page here just like that. That is the string representation. Now, we don't want that because that looks terrible. And instead of just showing the string representation of our class, what we can say is, Hey, wait till show the quote and the attribution as columns. Let's go ahead and add quote in lower case and attribution. Go ahead, save and refresh our page. And would you look at that? We have quote an attribution and it's sort of all. Now let's add some search because maybe maybe as a rocket maintenance company, we have so many customers I can only think of, like three off the top of my head, Blue origin, Space X and NASA, maybe Virgin Galactic. So we have four customers, but yeah, they each write us 500 testimonials. So we've got 2000 testimonials on her side, and we want to be able to search through them. What fields do we want to search through? We want to be able to search through quote and the attribution on what this is going to dio . It's a hey, I know that there's search built into this. And so, um, there it is the search bar and let's type in G R shows up with Ger. Let's type in Yoda, and it finds Yoda for us. But in the future, we're going to want to be able to select some of these. We want the content entry person or a client to say, Oh, actually, I only want this particular quote from Yoda to show up on our page, and right now they have no way of doing that in way. Tell, there's this feature called a snippet chooser panel, and as a stream filled, it's a snippet chooser block. And what that does is if we register this as a quote unquote snippet thes, then become reusable pieces of data with a graphical user interface inside of Wag Tail. We'll tackle that bit a little bit later, but for now, let's just go ahead and register this as a snippet. So all we have to do now is go back to our models dot pie and at the top here. We're going to want to register this as a way till snippets. So that comes from wag tail dot snippets. Top models, import register snippet, that one right there, and we can use this as a decorator or occasionally you'll see this used as a function. And then you could put your testimonial in here like this. We're not going to use it as a function we're going to use it as a decorator in Python because everything is an object you can use them sort of interchangeably. Use one or the other. Don't use both, though. Let's go ahead, Refresher page and all of a sudden way. See the section called Snippets Show Up. Now snippets again are reusable pieces of data. We have a section called Testimonials. It understands that we've got testimonials in here is called Testimonial is using the verbose name plural testimonials. It's click into that, and now, at this point, we can select a bunch of Leeds. Let's make that just a little bit smaller. Set looks a little nicer. We can select a bunch of these weaken, delete a bunch of them at the same time. We can edit them. We can delete one at a time if we wanted to. But the nice thing is, eventually down the road, just like a page chooser panel or an image chooser panel, that little motile that pops up in allows you to choose a page or an image. We're going to be able to say, Hey, show us a motel where we can select just testimonials, so that's coming up, down the road. So essentially what we've done is we've created a jingle model registered it with Wag Tail . So waggle is now our at men panel. So we don't have to use the jangle Admit we have added multiple columns. They're sort of all we've added search, weaken, search through either the attribution or the quote column And then we registered it as a snippet and now our data can be used in a graphical user interface or within something like the get context function or the get context method. 17. Welcome to StreamFields: Let's talk about stream fields. I've mentioned this over and over again. This is a concept that makes way tell extremely powerful. Now, stream fields are an insanely beautiful feature. They let you add sections of contents pretty much anywhere on your page. You can add unlimited stream fields, and your content does not need to be in any particular order. So if we view our Rocket Man page right now, we have header a hero section, nothing in here and then a footer section that is all in order. And that's going to stay that way because we needed that way. We need a constant template or a layout for our entire site. But right in the middle here we have all sorts of different content and we can mix and match it with a stream field. No one example of this al be It is probably a pretty ugly example. Is that one in here? Yeah, here we go. So, on the learned wag tail dot com website, this is a tutorial detail page that I have. It automatically grabs the tag name and the page name, and this one is a title. This is a stream field And then I've got a rich tech section in here so I can add links and images and things like that. And then I've got another title in here, some more basic text. And then I've got a code section and I can write any sort of code. I want again another title to more text and some more code. And I can mix and match this in any way I want. So this is No, this is not the greatest example. Let's see if I can find a better one. Okay, I found one finally, how to register a jingle model with wag tails model admin, which actually think was our last lesson. So if you ever looking for source code, it's on this page. So you can see here. I've got a title just as the jest and I've got a stream field where I can literally just put the gist. You are Ellen here. So I've got just your all, and it automatically does the highlighting and stuff from a get hub. You, Errol. I can mix and match us all they want. And just like that other page, I could move this up and down. I could add content between sections. I could add titles wherever I want. There is no strict formatting, and that is what a stream field allows us to do. But I guess an even better example would be, let's say, landing page. So let's say you have a landing page where you're trying to convert someone. In fact, I actually just thought of an even better example. So here's a wag Tail site. It's from amplifier giving dot org's so they've got a banner section here. This is not part of a stream filled because there's always going to be a banner here, but they can swap out the images and texts and things like that. Then we've got a section in here that they can move up or down. So they have this section of the three cards, and they could move it down, section up a section or delete it entirely. And then they've got another stream field here. Why started giving circle? They could move that up or down that can customize everything within it. Then they've gotta sign up, sign up for a newsletter to receive philanthropy, resources training and learning opportunities, grant opportunities posted by giving circles, and it asked for an email address. Then there's another section in here a stream field, another one in here and one of the bottom. And we could say, You know what? Maybe we want this green one toe. Also be up here right in the middle. Being able to switch your content around like that makes your pages really, really flexible while maintaining a nice design. Now, if you are already familiar with Django Stream fields, we're going to look a lot like a standard ah model mixed with the jangle form. But if you're brand new to jangle, your brand new to Wag tail is going to look a lot like what we've already worked with with a slight difference. And that's like difference, for the most part, is instead of saying models that text field, we're going to end up saying blocks, text block and instead of blankets false and Noah's false, we're going to be saying, Is it required true or false? Other than that, almost nothing's going to change. Everything we've been writing is going to be already very familiar to you. Now in this video, we're not going to be writing any stream fields. I just wanted to give you a quick let introduction into sort of what a stream field is and why it's important. And then, as one last sort of important notable stream fields and how they're stored in the database right now in her testimonial block or a testimonial model, we've got quotas equal to models dot text field in our database. Whatever database queen abusing this is going to be a text field or a text column with a maximum length of 500. The attribution is going to be ah, probably of arch our field or a regular character field with the maximum length of 50 characters you can also put into your fields. Positive interred your fields, Foreign keys many to many relationships. Things like that and those are all stored in their own columns. But a stream field is not stored like that. A stream field is basically all of your stream. Field data is going to be stored as a giant Jason object, and the reason for that is because this allows your page to look up all of its custom fields really quickly and maintain the order of all of your content while still only performing database queries when it's actually absolutely necessary to. But as a python developer that could make parsing your stream field data. Ah, little bit tricky because it's all J solidified, and it's a little bit harder to pull it all over the database the way you would expect. So there's mostly pros. It's 90% prose, but there is one little con there. If you're looking to really automate your stream fields and you want maybe someone to fill in a form on your page and create a stream field down the road, maybe that might not be the best approach, because that's going to be a little bit harder than just using a regular field and just updating a regular model. Regardless, let's go ahead and jump directly into streams, fields 18. Creating the StreamField App: like anything in the world of programming, there are several different ways to achieve the exact same final outcome. In this course. We've been putting all of our pages into their own APs instead of one big app called pages . But, hey, some people like to put all their pages into one app, and that's totally OK. I'm not going to tell you that's right or wrong for me. I like to split things out so that I can find them a lot easier. The same idea can be applied to stream fields, although I'm going to be creating an app just four stream field so that we don't run into circular imports and we have a dedicated area for all of our stream fields. It helps us find our custom content sections. Ah, lot easier, I find. So instead of spreading them out into stream, feels that are just for the home page without stream fields in the home app. And then we've got stream fields in the services apple. We've got stream fields in, like I don't know the flex page app. We're just going to put them all together so we can find them nice and easily So what I'm gonna do here is just get into my virtual environment. Source. Rocketman vin been activates. That's how we do it with. Then let's go ahead and do python manage dot pie. Start AP Streams and Streams is There's going to be a short terminology for the word stream fields, and let's go ahead and do Python managed at PI runs over and let's do that on Port 8000. Let's open up our code and we can see that there's a new section in here called Streams. Let's go ahead and activate this new app, so enter based up high in our installed APS. Again, we've done this number of times already. Let's go ahead and add streams in here. Save close that and let's go and look for streams Now. We actually don't need a lot of this. We don't need views. They're going to have their own templates, and stream fields are also going to be included on different pages as well. We're not going to be writing any tests, so we don't need to do that. Models while stream fields are sort of their own things, So what we're going to do is rename models. Two blocks. I guess you could call it models if you wanted to. I like Colin. It blocks APS. We're going. Teoh, keep admin. We are going to delete. So we have migrations in it. APS and blocks and in blocks. I was gonna delete that. And now we have what looks like a jangle app that we sort of mutated quite a bit. So we don't even have a model stop high file anymore. It's just block Stop, I Now, the reason I'm calling it a block is because every stream field, every stream field that we end up creating is actually a block of some kind. So maybe it's a char block or an image chooser block or a choice block. We're gonna go through a bunch of these different blocks together, though, but for now we just need to make sure that we have streams up and running, that we have streams enabled in based on pie and that we have a block stop. I file once you have that head on over the next lesson and we're going to create our first stream field 19. Your First StreamField: Oakley locally, let's go ahead and create a first stream field. This is going to be a basic text based stream field, So let's go ahead and open up our streams block. Stop, I file and let's create our first struck block. And what I'm saying here is struck block. Now we're going to create a struck block with just one single field in. It's just going to be a regular text field. And instead of using something like models Dutch, our field, we can use something like blocks dot hr block. And this is strictly wag tell at this point, this is always tell all the way. Now, at the root of it eventually breaks down into Django. But this is pretty much always tell right now. Now the nice thing about stream fields is they can be sections of content that can be ordered up and down so you can say, like hello, World is the 1st 1 My name is Caleb is a 2nd 1 I like goats is the 3rd 1 and it's just like moving this up and down. But you can do this with entire blocks of content. That's one real nice thing. The other nice thing about this is that stream fields could be nested, they can be reused, and actually, we're going to get into that a little bit later. So let's go ahead and get started with this. The first thing we need to do is import this. Let's do from wag tail dot core important blocks. No notice that this is using the word blocks and our file is called Blocks up High. There's no relation there. It just happens to be that I called this blocks up. I It does not need to be called block Stop I. This is simply where I'm storing this file, and Wag Tail also has a file called Blocks That Pie. Now let's create our first class here a title blocks. Let's call this class title block and is going to inherit from blocks dot struck block, and it's going to have one single field in here. Just text text is equal two blocks blocks dot hr block, and in here we can say instead of blank is equal to true or false or no is equal to true or false. We can say this is required. True or false. Yes, this is required. That's the default. Can actually see it here, says Char. Block requires equal to true. That's the default. I can also give us some help. Text max length, mean length, custom validators, things like that. Let's go ahead and give us some help. Text. It was going to say help Text is text to display. We don't need panels. We don't need anything like that. Wait till just automatically assume that if you have a stream field here, it should be shown. Let's go ahead and work with some meta data as well. So, first of all, we can give us a template, and that's going to be somewhere. We'll get back to that in a second. We can give us an icon. We'll get back to that in a second. We give us a label custom name if we wanted to. We'll get back to that in a second and also some help text, which, if you haven't guessed it, we'll get back to that in just a second. So the template is going to be just like our way. Tell pages if this was awake till pages would be title blocked out each to melon, it would live inside of the Streams folder because this is the streams app. So this is where it would look for it automatically. But it's not a wag. Tell Paige it's a stream field and is going to do something very, very similar. But instead of guessing, I always like to just put that in there anyways. So I'm gonna assume that all my stream fields are going to live in the templates folder in a sub folder called Streams, and this is going to be called title block dot html. I like doing that. So if you were to look at my code for the first time ever, you'd be able to look at this dream feeling ago. I know exactly where that template is. Let's give this an icon and edit icon, and the label is just going to be title. Otherwise, it might be called Title Block or anything else we want to call it, and the help text is going to be centered text to to display on the page and that it that's how we create a stream feel now that's not available on any single page at all. So if we go back here and we try to at it our home page, going to see that nothing's changed. We added lead text button button text, banner, background image, but we haven't added a new section in here. This is where the panel comes in to actually expose the stream fields to the admin interface. So let's go ahead and add this to let's just say the home page so that our homepage can have some custom content on it. I'm gonna go ahead and collapse that left panel there, so I'm just on home. Models dot pie and in here I want to do from streams dot blocks. That's my blocks file here, not the wait till core one. That's my blocks file import title block, Tyto Block. Alternatively, we could also do from streams import blocks and then a little later, instead of just using title block, we could use Block start title block. In fact, that's the way we are going to do it right now. So we're just going to do from streams import blocks next in our home page down here. Let's go ahead and add some custom stream fields. Now a stream field is its own field type. Just like we have a char field or a text field. This comes from Wag Tail. This is a stream filled. So it's called this body. That's what we're gonna call it. This is going to be a stream field, and this is going to take a list of topples. So it's a class. Its first parameter is a list, and in here it's going to be a bunch of topples. So let's call this one title. It is a to set topple, so the 1st 1 is going to be what it's called in the database. So again, remember how I mentioned that wag tail stream fields are all stored in Jason? Well, that's the key. That's how it knows what it's going to be. So we're gonna call that title. You can call it anything you want. It doesn't have to be title, and we're going to say blocks dot title block with parentheses because it is a class. I just throw a common there for good practices, I guess. Are we gonna let this field be knowable? Yes, we're going to say that the home page might not have any custom content on it and kind of be blank. Let's also say yes. That means you can save the home page without adding any extra stream fields are or custom content to it. Now we have this body. What do we do with this? Let's go down to our content panels here, and we need to expose us with a new panel. So is create a new line of even move that up a little bit there. Let's create a new panel called a Stream field panel, and we're just going to put body in here now. If we save us, it's likely that our terminal is going to complain because, first of all, that was a table. It's gonna complain about stream, field panel and stream field not being imported. So let's go ahead and make sure those air imported. So in our edit handlers up here, we've got field panel page chooser panel that's at a stream field panel in here, and the stream field itself actually comes from Wag Tail decor dot fields Import stream field. All right, let's say that and see Hey, look at that terminals not complaining about anything. Let's go and view the page and, as expected, operational air. No such column. Home home page dot body. Basically, this is an SQL area saying that our home page does not have a column in the table that it lives in cold body. Let's go ahead and make our migrations make migrations, apply migrations, rerun our server. Let's go and refresh your page and we're going to see once it loads want loads, Scroll all the way down. We've got a new section in here now, if you were using Wag Tail 2.6 or earlier, this is going to look slightly different and for using Wait till 2.7 or newer is going to look something along these lines. So I've got a new section here called a title, and I could put anything I want in here. That's a wheel. Make your rockets more fuel efficient, centred text to display on the page that their help text. We've got some more text in there. It's a required field. Let's go ahead and save this. Now, when we view this, we're not actually going to see this at all, because on our home page template, we haven't specified that we actually want to show this at all. So our next step now is open home page dot html. That's not the right one at all. Home page dot html That's our homepage. And on our home page, we have a section for our banner. I'm gonna make that just a tad smaller there. So I've got a section for a banner. Let's collapse that and let's create a new block for a content. And again, just as a quick little refresher here. If we open up based on HTML, we've got a section in here just for a content. We're going to inject our stream fields into here, and to do this is actually super super easy. We just need to basically loop over each one's, a four block, in page dot body include Block the block that were looping over. That's that value right there and and four now for this toe actually register because this is a wag tail function. This doesn't come with default, Django. We need to make sure that we can use us. So let's also load along with our wait till images tags wag tail core tags. Let's save that and let's refresh your page. And now we can see that the template does not exist, so we're actually getting somewhere. This is saying that our Streams Air title block does not have a template yet. Let's go ahead and add that template. So open up my website and then go into templates. And in here it was looking for streams slash title block dot html. Again. Where I got that from was I specified it specifically to live here, and you can see where it's looking. It's looking in templates. Streams title blocked out. Html It's also looking in home templates. Streams Title block. Daddy, She male. It's also looking in search template streams. Title blocked out. Html Let's just put in. The first place is looking That seems most efficient. So we've got a title blocking here. Then let's just do something real ugly. H one, this is a title block refresher page and oh! Oh, there it is. There's a title block. Okay, Okay. We're getting so close to this. Okay, so I just threw some html in here with an H three and a custom class on that age three. And now I could do say anything in here. All right? No says say anything in here. It's looking a little more styled. Let's go ahead and actually throw that text in here when you use value in lower case dot text. Now, if you're wondering camera, where did you get value from? I can use value or I can use self, which everyone makes more sense to you there the exact same. And that's actually coming from our block itself. So title block and put these together Title Block is using this template and inside of it, just like page dot text or page dot banner title. This one has title block dot text. Now it's not a page. So we're not gonna call it Paige to stream fields. We're gonna give it the value. The value is whatever the stream field is or self dot whatever the field name is again, I'm going to call that value. Let's go ahead and refresh this page. Well, make your rockets more fuel efficient, and that is exactly what we put in her home page. Now let's take a look at the rial reason we're actually using a stream filled for this Is I put anything in here, I'm gonna put alarm ipsum in there and below it. I'm gonna put this is below the original one. And let's preview that alarmism at the top, the original one and one at the bottom. And if I didn't like that, I could always move this one up. And so the original one is now at the bottom hit preview again. Scroll on down. Well, make your rockets more fuel efficient. It's now at the bottoms. Now we're taking these entire sections and moving them up and down. This is just where it starts because this is just one title field we could do is with entire sections of content with images and videos, documents, links, all sorts of things. I'm gonna close that preview and I am going to I'm not going to save this page now. One last thing. When you're working with stream fields because they are a special, complex type of field, it doesn't just have a regular column with a single value, and it is a big Jason blob. It's always a good idea, and I don't think this is going to need any migrations, but I think it's always a good idea to when you're done working with them. Always do managed up high, make migrations no changes detected. But if you've been working with them, if you've done anything extra, you will have changes detected, in which case you will also run. Python managed up high and to run them, you use migrates. Correct? Yeah, use my great, and that will apply your changes for you. Let's move on to the next one, where we can create sections of stream fields where we can add more and more and more and more like a list. It's actually called a list block. So once you're done playing with this, if you want to definitely feel free to experiment with a code and when you are nice and ready, let's head on over to the next lesson where we pick up our next extreme field type. 20. Repeating StreamFields: There's going to be a time when you need to repeat similar content over and over and over again, sort of using the same template and swapping out different parts of the content. Lists of really anything are really good candidate for this thing called a list block. So in the last lesson, we learned about a struck block. This one we're going to use a list block, and this one has some weird looking syntax. But it is a very, very powerful feature in wag tail stream fields. So on our website were going to be creating a repeating content block where the content entry person can add on limited items if they wanted to. But they're all going to be formatted the exact same way. And we're going to be doing this with bootstrap cards. So if you don't already have your server up and running, make sure you get into your virtual environment and you are running your Django server. Next, I'm going to open up block Stop I That's in my streams application. So streams slash blocks that pious where this is that and this is where we created our title block. Next, we're going to create a new type of stream field. This one is going to be called a list block. It's actually still technically a struck block, but we're gonna throw a list block inside of it, and then we're going toe loop through it over and over and over again. So you can think of a list block, really as a really fancy list. And then inside of this fancy list, we're going to have another struck block. So again, the syntax gets really weird, and I'll try my best to describe it as we go along and this is going to at first. Not only is it gonna look weird, but it's going to sound a little complicated. Probably already sounds complicated if you're brand new to Wag Tail. But once you write a few of these, they actually start to make a lot of sense. So let's start with a very simple example. Let's add a title char block and a text text block and add ah meta class Wissem some data in there. So let's go ahead and create class. We're gonna call it cards block because that's what it's eventually going to be. Calder. That's what it's going to be doing. And in here, this is just going to be a struck block, a regular struck block. Now, here's where it starts to get a little weird. So we're gonna have cards in here, and this is going to be blocks dot list block. So this is already starting to look a little bizarre. And then inside of this list block, we're gonna do block start struck block. Now, this is where it gets weird because our classes of struck block were defining a list block . We've got another struck block inside of it. And in here, this is where we're going to put all of our custom fields. So this is a list of couples just like a regular stream field, just like how On our home models that pie we have stream field and it is a list of two pair troubles. It's going to be the exact same thing. Here is a list of two pair couples, so the 1st 1 is going to be a title comma, and then we're going to use blocks dot HR block looks a lot like blocks HR block from our title block. This is going to need a max length. We want to give us a max length of maybe 100 or so, and we can give it some help tax, so help tax is going to be Eventually. This is going to be the Bolds titled text for this card, and I like to put what the max length is so that your customer, your clients, continental people know what to expect. So there's a max length of 100 characters Now if I really use remote, I mean, there's a really long line, and eventually I would break this down. But for now, I'm gonna keep this on one line because I'm also going to create some text in here on a second line. If I put this on too many lines, that starts to look a little complicated. So this is going to be blocks dot text block. So instead of ah, text field, it's a text block. Max length for this one is going to be a whopping 255 characters, that is all, and the help taxes going to say the optional text for this card, The Max Max length is 250 characters, and let's also say that this is optional by saying required is equal to false. It was as, um, metadata in here. So class meta, I'll make that bigger one more so we can see that a little easier, you know, collapse that. They're So we've got class meta. And here let's specify template, just like we did with her last one. We're gonna say it goes into streams and this is going to be a cards block dot html and something is not right because my syntax highlighting is broken. What did I break in here? There it is. I am missing a bracket that looks better. I'm going to give this an icon of an image and a label of ANA standard cards. I mean to say that make sure my terminals not going toe complain. And sure enough, it is about a stuck block. I think that's when I made the original typo. Let's go ahead and restart Django. And nope, sure enough, stuck block is not a thing. Where is stuck? Block? There you are. I was looking at the wrong one blocks that struck block Okay, terminal and is happy with our code. Let's also go back to our home slash models dot pie. You can see that I'm in the home folder models dot pie, and now I'm going to create another two pair toppling here, and this one's going to be cards. And this is going to come from Block Start cards block and again. Just put a comment The end for good practice. Let's go back to our home page and let's go edit it. Gonna wait for it to load. Let's create a new one in here and look at that. We have a title block, the one that was created in the last lesson. We also have a standard cards Now. This is where it gets really cool because this looks a little nested now, So this could be title number one, and text could be any sort of Texas completely optional. So let's say the 1st 1 is going to be optional. Title one with optional text and let's go ahead. Hit that plus sign and create another one. And these are actually and totally swappable, too, so we can swap these up and down, and this one's going to say, title number two, because I'm not feeling creative with my titles and this one. We're gonna have some warm ipsum text in there. Let's go ahead and save this. Now I'm going to view live and it's going to say I know it's going to say this can't find the template. Sure enough, look at that template does not exist. It's looking for streams. Cards blocked out. HTML. Let's go ahead and create that file now. So in a rocket man templates, we already have a folder called Streams. Let's create a new file in here called cards block dot html and let's just refunds is Let's just loop through this just real quickly. We're not gonna make this look good yet. We're just going to loop through this, so I'm going to say four every card in value dot cards. We could also do self dot card, so I'll use these interchangeably throat different stream fields, and I'm going to end that. So what I'm doing here is I'm saying in this particular stream field, no wrong file their Rio. In this particular stream field, I have a variable called self dot cards. Self dot cards. Object oriented programming dictates that if I were to access this in a method, it would be called self dot cards. It's a list. Block is right in the name. It's a list, so it's an irritable weaken loop through it so we can say for each card in self dot cards. And then we can go and get each one of these So we can say the title is going to be an H three card dot title. And we can also say, if card dot text we can put paragraph in here card dot text and that if and let's also add a bunch of horizontal rules to make this really, really ugly, but also very noticeable with where we're working, Go ahead and refresh this page. Look at that title one optional with no tax. There's no tax in there, entitled to with some text in there in a bunch of horizontal rules to make the page super ugly. Now let's back this up. Just a quick second here in her models don't pie. We added something to our body field. Why did we not apply migrations? Well, because this is just basically a text field. Storing a lot of Jason in the database doesn't necessarily need migrations to be applied, although chances are, if you were to try to run migrations right now, it would say that there were changes detected. We're going to run migrations at the end of this lesson anyways, but we don't need to do it right now, all right. I think we have a little bit of time in this video still, so let's go ahead and add a couple more items in here. So we're going to say every card as a title. Every card has, um, text. Let's see. Every card has an image, and we're going to use an image chooser block we have not imported this year, not a panel image. Chooser luck. This is going to be required. Two required by default so we don't have to put required is equal to true help. Texas Eagle to Image will be auto magically corrupt to Ah, if I remember correctly somewhere around 570 pixels by 370 pixels and we're going to set this cropping in the template, let's go ahead and save that and let's refresh our creditable page or home page. Oh, it's not going to load because I didn't add image chooser block, Let's go ahead and add that in here now. So we do from way tell dot images, duck blocks, import image, chooser block. There we go. Now it will connect. And if I scroll on down, we have a little image section in here. So I'm gonna say that's gonna be the 1st 1 And this is going to be the 2nd 1 going to go ahead and save that. It's going to when I refresh the page is going to loop over everything. There's no images because we didn't tell it. Toe have any images? Let's let's go tell it to have some images now, before we can use images at all with Wag Tail and automatically cropped them and all that good stuff, what we need to do is say, load, wait till images tags, and that allows us to use a template tag called Image. Let's put that in a for loop. And because we know this is a required field, it will always be here. I'll say four. Every image that we have we're going to use the image tag card dot What did we call it? What did we call? Probably just image. Yep. Sure enough, just called it image. So I just got that from the first value in our double sets of couples here. So the first set in our Topple is called image. This one's called Texans called Title. That's where we're getting these from. So this one just called image, and I'm just going to use that there. I'm going to use the film method. There is Max with methods, height and width methods as well, so you can set a maximum height or maximum with on your images. Things like that were going to say Always feel this toe. Always be 570 pixels by 370 pixels. And then we're going to simply store that as a variable called image. Let's go ahead and create a new image in here and for the source. Let's do image that you are on the altar is image dot alta, and so I am G matches here, here and here. It has nothing to do with the HTML tag, and in fact I can change that just to prove it. We'll call it the image. Let's go ahead, save and refresh our page, and we're gonna see an image there it is. There's our 1st 1 and there's our 2nd 1 And if I inspect this element here, sure enough, there it is. 5 70 by 3 70 it took my image and it cropped it perfectly. Now I think that's enough for this particular video. In the next one, we're going to start abstracting some of the more complicated parts into other classes that we can reuse. So in our blocks, we don't necessarily need to put all of this in here. We can make it look more like this one up here because this one down here is honestly, that's kind of hard to read. So we're gonna touch that up a little bit. And I also mentioned, at some point we can run migrations had. Let's do that. Python managed up high. Make migrations. Let's see what happens. Sure enough, Alter field body on homepage and let's apply that migration. Everything is well. Don't forget at any point time. If you get stuck, always look at the source code. It's completely available to you. Otherwise, let's head on over to the next lesson where we continue to operate inside of our list. Block 21. Simplifying Repeating StreamFields : all right. Picking up from the last lesson. This is the second part of the repeating stream field lesson. This is a fairly big topic, so it's split into at least two videos. Now, if you haven't watched the 1st 1 you're going to need to watch the 1st 1 Otherwise, you're probably not going to know too much about what's going on with this one in this video. So now that we have a simple list block style stream field, basically we've got struck block with this block, instruct lock in it and then sets of temples in here. Let's go ahead and make this even more complicated, so the first thing we're going to do in here is add a link and we need to add three fields . We need to add a link text because we want the text to be customized. We want an internal link to another wag tail page or we want an external link to a page. It's not on our website, so let's go ahead and sort of just hash this out very quickly. So we've got block start HR block, and we can fill this out in just a second. Let's call this link text. The 2nd 1 we're going to call Internal Page, and this one is going to be some sort of paid shoes or we don't really know that one yet. We'll come back to it, and the 3rd 1 is going to be an external link. And in here, this is Block. Start your L block. Just like, uh, you are all field models that you are all field. This is a blocks that you were a block. Okay, So is the external link going to be required? We're gonna say no, because the links are going to be optional entirely the internal page. How do we choose an internal page? Wait till gives us a beautiful little tool here called the Page Chooser Block, who can also say required is equal to false Make sure you gotta calm at the end of that and our link text. We need a max length. Let's say 50 and default is going to say more details. Also put a comma at the end of that. Let's see. Okay. Presumably I don't have any typos in there. Let's go ahead and edit our homepage. And in here we can see there's link text and internal page, an external link. So on internal page allows me to choose the home page or the Services page or the about page or any of the service detail pages. So for this one, I'm going to select cleaning. And for the 1st 1 I'm going to just put an external you earl in here. So, http wag tail dot io All right, that saved. And when we view our page again because we haven't modified anything, a template, we're not going to see any of these changes yet. So let's head back on over to our template and let's create a Lincoln here so it's create an A tag. The draft is going to be a little bit of template logic. So let's let's leave that for last, I suppose. And in here, let's put our button text. We've got some Texan here called card dot Link text, and where I got that from was for each card in cards. So that's where I'm getting card from. There's title text, image link text, internal page, an external link. I'm getting the link text from right here, and I'm just throwing that in there so Let's go ahead, save that and refresh our page. And we're going to see more detail for detail. And in fact, that is supposed to say more details. But that was already saved. So that's too late. Ah, and now we need to check to see if we have an internal page or an external link toe to use . So let's do this in here. We can basically, right. I'm gonna write this out the long way and then I'm gonna shove it all into a one line going to, if card dot internal page car dot internal page that you are l. Because it's a page object and page objects always have dot u R l In them, they always have the UL property. Then we can say, LF card dot external page, Where does that come from? There you are. External link is what it's called. Then we can say card dot external link that one doesn't have a dot UL because it's just a regular text field. And if that's what this is going to look like now I'm going to start toe jam all of this into one line. The fifth ISS and copy all of that and I'm gonna throw that into the A draft. Here it's Go ahead, save and refresh that page. And if we look at the bottom left, that is in fact trying to go to wag tail dot io. That's perfect. That's where the 1st 1 was supposed to go and this one is going to go to the cleaning page . So now we're able to actually add links to our stream field inside of our list. Block can go to any page inside of white, tell any page outside of wag toe. Now, let's go ahead and make this code a little nicer to look at, because if we look at it right now, I mean, this might be readable for a lot of people, But to me, this sort of just looks like garbage. Like if you showed me this code at first, yes, I would read it, and I would be able to parse it with my own brain. But just looking at, I just go. What is what is all that stuff down there? This this doesn't really look great. I don't really want to tackle that. So let's go ahead and sort of like extract all of us, and we're going to put this into a new class called a card, and this is going to be a struck block, and what I can do is I can take all of that copy and paste, and we cannot use it in this particular this format. But what I can do is I can use my editor to help me sort of cheat a little bit, and I can at its multiple places at the same time. And we can do this. We can select all the blocks that's just grabbing the R block. Let's grab all the blocks. But these all in new line, that's not quite gonna work out the way I had hoped it would. So let's go ahead for this on new loan. Get rid of those two lines. All right, so here you can see, I just put everything sort of on their own line, and it looks a lot more like our title block. It looks a lot more like a wag tail page looks a lot more Django friendly, and it doesn't need to look like this anymore. Now we're not actually going to enable this, as is own stream field. We're just going to use this in place of all of this stuff. So what I'm gonna do is I'm going to delete that, and I'm also going to delete this and I'm simply going to put card. It is a list block of cards. Now, when I go back to my browser and I'm gonna edit this page Ah, I still have all these cards in here. So I got the title, the text to image the link text, internal page, all that good stuff. So just to make sure that this is working, let's go ahead and throw something crazy in here. Zebra, zebra, zebra. I don't know why I picked a zebra and let's go ahead and refresh your page and look at that . So now we've entirely actually we've we've entirely removed all that noise, all that no easy code, and we've put it into its own class. Now, the nice thing about this is we can actually go one step further and we can take out our link as well because we're going to be using that in several different places. So I'm actually gonna cut that and I'm going to create one more class in here class. It's just called a link blocks dot struck block and throw that in there Now, a little bit later, we can actually even get one step further, and we can add validation to this. So any time someone uses a link on any Stringfield anywhere, we can say that the internal page or the external link have to be. Sadly, one of them absolutely has to be set. Currently, they're both not required, so you could theoretically have a section without a link. Well, you don't really want that at all, so we could make sure that that's always said We'll talk about that down the road. So we now have a struck block for a link, and instead of our card having the three fields in here and sort of looking fairly long, we can know just say link is equal to our link. And is there going to be any sort of help text? We could do help Texas equal to enter a link if we wanted to enter a link or select a page ? So now if we rewind this, our cards block is a list block of cards, and that's just using this. Every card has a title, text, image and link. And now that link also has a link text, internal page and an external link. Let's go ahead and edit our page. We are going to see one change in here, whereas if I keep scroll and down, keep scrolling down. We've got a link section in here. Enter the link or select a page with link text, internal page and the external link. Now, if you remember, those links were actually working and you can see at the bottom left here. I haven't refreshed my page yet. It is working. It's his way. Tell dot io and if I refresh my page, we're going to see that link no longer works now. The reason for that is because we said in the card block, we no longer have car DOT external link. We have card dot link dot external link or card dot link because it's thinking to another class dot link text. So let's go back to our template and where it says car dot internal page or car dot external link. We're gonna grab all of those and do card dot link dot internal page card dot linked out internal page dot You are Oh, so it's getting kind of long. Were sort of starting to traverse through our stream field into Wag Tell itself or we've got card that link dot external link. Let's go ahead and save that refresh our page and you're going to see that this still doesn't work. Now it's at this point you might be thinking that something is broken and you wouldn't be wrong to think that. But the reason this doesn't show up again is because of what we originally saw. This is actually it's own section now, so link text technically does not exist. It is linked dot link text. It is linked dot internal page. It is linked dot external page. So if we choose a page here and this one's just going to go to home page and this bottom one is going to go to wag tail dot io and let's put on external Lincoln here. Http wait till dot io save in refresher page and they still don't show up. And I am willing to bet that they are in their working and that I had just made a mistake. Let's go ahead and dissect this. I bet you I made a mistake somewhere. And if you're screaming at your screen saying Caleb, I know your mistake is that's awesome But I'm going have to find it low. Look at that. There is the link. No text in there. That's interesting. It's probably the exact same thing down here. This is gonna goto yet. Wait til dot Io no link. So that is my mistake, and my cursor was already on it. It was card to dot link darling text. Let's go ahead. Save that refresh one more time. High like that. Sure enough, bottom left, says wag tail dot io. That's where it's gonna take me when I click on it. It's going to take me to wait till dot io Check that out, and that top one is going to take me to the home page. So as a quick recap, what we've done in these two videos is we have created a struck block with a list block inside of it, effectively creating a list of struck blocks. We originally created a fairly long form struck block, and then we sort of abstracted it out of itself into its own class. We call that class card and said, Just make a list of card struck blocks. But we also had links in that struck block in the card struck block, and we said, That's that's too much. So let's go ahead and actually turn those links into their own class, their own struck block, which is what we did up here. And we threw those in there. But now, because we have for each card in self dot cards and we have link going to another class in our template were using card dot link dot link, text card, darling dot internal page. And it's really just saying for each card in cards dot link dot link text and that, my friend, is a list block. So again, the syntax that versus pretty weird I'm not gonna lie. It's pretty odd, and it's a little confusing at first, but definitely give it a shot to try to make a few of your own. And when you start to make a few of these on your own, you start to look really nice. Now, this is not everything I've wanted. This is not a nice looking stream field. So what? I'm going to Dio is I'm actually going to fade this out and I'm going to throw in a bunch of HTML and we're going to fill this in together. All right, So I just added, Ah bunch of HTML in here and we're gonna fill this out together. So this is just a standard sort of bootstrap block. So it's probably not going to look the greatest at this point. Yeah, here we go. It's gonna look just like this. So we've got a card on the left card on the right because we have two items and our list block. We've got a title, some text, Ah, button and link. And this is supposed to go somewhere. So let's go ahead and actually do something with this. So what's the biggest win we can make here? What is going to really liven up this display? Let's add an image. So we have our wag tail image tags loaded. Let's go ahead and add our image Image card dot image. And again, I am getting that from card dot image. Phil, I've got a placeholder in here for 5 73 by 3 69 Let's do 5 73 by 3 69 Although I think that helped. Text is actually saying something different, isn't it? Yeah. 5 70 by 3 70 Let's just make that right. 5 70 by 3 70 as image and let's replace this. I am G. Don't you are l and the altar. Let's change that to I am GED tot not you, earl dot lt Refresh your page. Look of that. It's already starting to look a lot better. Let's have the title next. We had a title in here. This is just going to be the card dot title, and again, I mean, this might make perfect sense to you, but I'm really going to drive this home. Where do I get card? That title from, I mean the list block. It's using a card class card dot title. That's where I'm getting this from the text we can do card dot text. Let's make that lower case, and is that optional? Sometimes I make that optional required false that is, in fact, optional. So let's throw an if statement in here. Let's do if card dot text. So if there is some value in there and if and let's refresh our page that's coming along pretty nicely so far. Let's now add that link. That link is going to be the Link text card dot the link dot link text. And again, where did I get that one from? I've got a card list block here. I'm looping through each one called card. Still four card in cards. It card dot Link is in here, so each card dot link dot link text that's where I'm getting this from. And now I need to fill out these links thes age refs. So let's do a little logic in here. Let's do If card dot link died internal page, then we're going to use the internal page you or else we're gonna do card dot link internal page dot You earl else. If your elliff card dot linked that external link, is that what I called it? External and internal page external link. Let's go ahead and do card duckling that external link and lastly soon else statement in here. Let's make it go. Absolutely nowhere with a number sign. And if and hopefully I got all that syntax, right? If I didn't Jenkins gonna let me know because there is going to be a a little hair on the page. Sure enough, nope, That all worked. Although when I hover over here, it says local host of the bottom Left, It says local host card dot link dot external link. Something is not right that supposed to be right there and check that out. That goes to wag Tail. Everything here looks nice and custom. This one's going to go to the home page exactly as I expected. Click that. Make sure it does go to exile dot io and look at that. We have got nice cards going on repeatable cards. We can add as many as we like. You could add hundreds or thousands of them if you wanted to slow down your page after a little while. But you can add as many as you like. I'm going to leave this at two and moving forward through this course. I might actually touch up some of this content as well, so you might not actually see in like the next video that this says title to it might say something a little bit different. Next. I think we should continue on this rule that were on, and we have a little way of adding some extra logic here. So instead of saying if card dot linked dot internal page else card not linked on external link yada, yada, yada, all of this stuff, instead of saying that we can actually shorten that up, I think we should go ahead and do that with a thing called a struck value. We'll do that in the next video, though, so once you're ready to move on from this one head on over there and we'll learn about simplifying some of this template logic. 22. Custom StreamField Logic: with a struck block, we can add custom, logic and values without having to actually map it back to a stored data object, which is really just fancy talk for saying we don't actually have to store extra data like we don't need another fielding here called, like the official you, Earl. What we can do is we can add some additional logic in here now, In Django. If this was a model, this would look something like class. You're model models, that model. And it would be like you've got a property in here deaf, something self return. You know, your logic would be in here, and then you would return. Whatever else is in there. Basically, you would use the at property decorator. Well, we're not doing that because we're not using at property. We're not using a jangle model. We're using a wait till struck block, extreme field section. So essentially, what I would like to do in this video is get rid of pretty much all this logic and just say if the car darling you are el exists, use that you, Earl, whatever it's going to be. So the temple he no longer needs to check to see if its internal page or an external page it needs to be one or the other, and we're going to do them with a cool little thing called a struck value. So in our link, we can actually say class Meta. Let's give us a value class and we're gonna give us a value Class of link value, link value. No, this currently does not exist. The link value does not exist, but we're going to make it exist. So up here, I'm going to say class link value now is going to exist. Blocks dot struck value, not instruct Block nor a list block, but instruct value. And this is going to add additional logic for our links. And so what I want this to Dio is instead of saying, link dot internal page, I want our template to be able to say link dot you Earl, and it's going to figure out which one is here, and it's going to just return whichever one is available. Now, this is actually quite simple. We have to do is type. You are l self, they're deaf, You, Earl, and let's go ahead and get that internal page to the internal page is going to be self. Don't get Internal Page and because we've linked this value class to delink value right up here, whenever dot u R l is going to be called, it's going to say, Hey, grab that internal page, whatever that is, and store that in a variable that we now have access to that let's do the external link. Let's do external link is equal to self dug yet ex eternal link. And I like putting these in their own variables just because it seems to make my code a little easier to read a little bit down the road. So now I can say, if Internal Page, because this is a local variable, I can do that. If Internal Page is actually set, I could just return internal page, don't you, Earl? And it looks exactly like what's in our template we've got If car dot linked on internal page, then show car dot link dot internal page Here it is dot u r l. We could do the exact same thing in our python logic. We can also do LF external Link and we could just return that as well External link. And lastly, if we wanted to, we could just return nothing. And if we wanted to him to this a little bit, we can say this will always return a string. Let's go ahead and say that. Check out our terminal. There's no complaints, so I don't have any typos. Hopefully, let's go back to our cards in here, and I'm gonna put this near the title just so. It's a place that I can see put horizontal rule in there and let's do card dot link dot you , Earl. Now the reason I'm using car dot link that you are only is from the last two videos where we're talking about a list block. And so now we have a real chain of events going on here we have a struck block called cards block, and inside of it is a list block of cards. Inside of each card is a title text image and a link, and inside of that link is three more fields, link text, internal page and external link. But we're also saying, Hey, there's a value class here, so give them some extra logic so that value class that link value is gonna come up here and any time we do dot u r l on the link itself. So this is just like adding you were all is equal to some custom logic. It's just like that that custom logic is now going to look for the internal page. If it exists, it's going to give us the girl. If there's an external link instead, it's going to give us that. And if there isn't one is going to return just a empty string. How do you like that For a chain of events? That's pretty good, isn't it? So now we're doing card dot linked at u R L instead of car dot linked on internal page car adult thing dot external link. Let's go ahead and give us Ah, quick, A little refresher. This one says Slash, because it's going to the home page. And this one says http wag tail, don't Iook because there is no page set for this one. It is an external link. So now let's go clean that up. Let's get rid of that. And let's select all this logic from our last video, all three sections, and we can simply do if card dot link dot u R L card that link that you are well, otherwise there is nothing in there. So, in fact, we don't even need to do that at all. Let's let's just do this. Let's always put that link you, Earl. So even if there is nothing in there, that's totally okay. Sure enough, the left one still goes to the home page, and the right one still goes to wag tail dot io. And at this point, we can actually make our template look and feel a little nicer. I like doing this part because we can say at the beginning of our A tag if card dot link dot you, Earl and let's go to the end of it and do And if and let's also go and look for the closing tags and basically do the exact same thing If card dot link dot u r l. And I'll explain what I'm doing here in just a second. And if so, this is saying if there is a link, you well, so basically, if it returns anything other than an empty string provide us with a link with that link you where l inside of it and likewise down here were saying, If that link you where all does exist, also, close that link. So now if we save this page, we're going to see absolutely no difference. Thes both have links. These are both links. You can see at the bottom left that they're switching back and forth. But just for fun, Z's let's go ahead and get rid of the one that links back to the homepage. The one This is Zebra, Zebra Zebra, and this one is going to the home page. Let's clear that choice. So it's not going to the home page, and it's not going to any external page or internal page. It's Click publish and refresh our page, and this one is not a link. Actually did break something there, though, and that was because I was trying to be fancy and edit three places at once. And sure enough, the card on the left no longer has any links anywhere, and the card on the right still does. And that is how we add extra logic to our stream fields using a structure value. Now it doesn't have to just be with a link. You can do this on any struck block at all. All you have to do is do value. Underscore class is equal to whatever your link value is going to be your or really whatever that struck value is going to be called that class name, and then you can grab your values from whatever field you have written. Then you can perform some logic and return anything you want. Personally, I think this is a fantastic feature that I don't see enough out there. I really, really like this one. I think it's a nice, clean way of giving your stream fields a little extra logic without cluttering up your template. At this point in time, I would highly recommend taking a look at the source code, even just feel free to totally steal my source code and just tinker around with it. Experiment. Try to break it if you want to, because there are four sections to this. There's the cards block. There's the card block, there's the link block. And then there's the link. Value has struck value, so definitely give that a shot. Have some fun with it again. Don't be afraid to break it. And when you're ready to move on to the next dream field. Let's go ahead and create a stream field where we're gonna have an image and some text. 23. Image and Text Block : All right, let's take a step back and not work with something so complicated. List blocks. They have, Ah, lot of parts, and it's pretty easy to extend it. As we've seen in the last three or so lessons, let's go back to a regular struck block and let's just add an image a maybe some image alignment options. We're going to reuse the link that we used in our created in the last video, and that's also add an image. So first, let's go ahead and open up our streams blocks dot pie file, and I'm going to collapse that entirely. Make that just one smaller, and it doesn't really matter where you want to put it. I guess I could put this at the very bottom. I know want to create a brand new class, and I'm just gonna called image and text block because I don't want to name these very creatively. I want to name these verbose Lee, so people know exactly what they're looking at. This is going to be a struck block, a regular struck block, and I know it's going to be a struck luck because it's going to have multiple values in here. So it's going to have an image image alignment. Let's give that section a title, some text and a link. Well, we know Link is already done. Link is equal to link. Now where does this come from? We created a class up here called Link. We used it in our card. We can now reuse us all over the place. It's a struck block, and it already has custom value called dot u R L. So now I can use that anywhere. So this one, when it comes to adding but in text selecting a wag tail page or an external page are copying and pasting a link to an external page. This is already done. That's three field in one line. That's nice and easy image. We've used this one already. Image chooser, block, and we could give some help. Text on this one. This is going to be required as well. So let's say the image will be auto magically cropped to. This is going to be a larger image. So I believe it's 7 68 No, I said that wrong. 7 86 60 It seems weird. 7 86 by 552 pixels. If I remember from the design correctly image alignment, this is going to be a new one. This is going to be blocks dot choice block now. A choice block looks pretty interesting. So we've got choices in here, and this is going to be a couple of doubles, basically. So you've got choices we can put left and left, and I'll tell you what that means in sec with the left, one on the right, one means right and right, and that was very confusing What I said. So each trouble has two values in it. The left one is what we're going to have access to inner template and the right one is what's going to be displayed so we could say image to the left. Totti left to the left and image to the right. That makes it a little clear. We can also give it a default value, so the default is going to be left. Let's say the image is always to the left, and like all of our other ones, we can also give us some help. Text in here. We can also split this onto new lines as well. So it looks a little more consistent. Help Texas equal to image on the left with text on the right or image on the right with text on the left. Our title is going to be blocks Tut char block. We've seen this one before. Let's give this a max length of something like 60 characters and help text max length of 60 characters. Text is equal to block stud. You want to HR Block Yes, is not going to be very long. Text. So HR block with max length of what used to be a tweet. 140 characters, and we're gonna make this optional required is false and we've got a Lincoln here. Next, let's go ahead. Specify some meta options. Where's that template going to go? Let's say streams. Image and text block dot html and icon. I'm going to give it an image icon, and the label is going to be image and text with an N percent in the middle. So it doesn't say and it just says image ampersand text. Now that's all groovy. And hopefully I don't have any typos. Me terminal doesn't think so. I'm gonna copy this value image and text block just that text and I'm gonna open up home models that pie and in my body here, where I have the title stream field and the card stream field, I'm just gonna add image and text blocks dot image and text block. Now there's currently no template, but let's go ahead and see what this looks like in our back end. When we edit our page, someone wrong there block is not defined. Yep, that's correct. Block is, in fact, not defined its blocks. It's refresh our page, and when I click this plus, we now have image and text noticed the ampersand there that is the label. So now we can select an image, any image. It's like that one. Do we want image alignments to left or image alignment to the right? Let's the image is going to be to the left sample title in here. Sample texting here. Sample text in here. Link more details. Yeah, let's go with more details. An internal page Let's go with shining. In fact, let's change some of this. That's a title the cleanest rockets to ever exist, and the text is going to say no rocket has ever been this shiny. It's so shiny, it's almost frictionless. Go ahead and save that. Publish our page and again when we hit Refresh. This is going to look for a stream field template that does not exist. Yep, and it's looking for image and text block, and we know that is going to look in all these different places for it. So let's go ahead and make that exist under my ROCKETMAN project or whatever your project is going to be called or is called under templates under streams, let's create a new one in here. Image and text block dot html. No, this point. There's nothing in here we have not dealt with to the exception of image alignment. So let's tackle image alignment first. And then we will, uh, I guess we'll go through all the other ones and sort of feel this. I wouldn't make it look nice. Let's look at her image alignment. All we need to do for this It's a self dot image alignment, and that is going to match this text right here and again. When you're in a stream field, it could be value dot, image alignment or self. That image lineman So let's say aligning. Let's give this in H one. And when we refresh our page do, do, do, do do it's going to be aligning left now it has nothing to do with this section. Don't be confused by that. This is going to be its own section right here. This isn't a completely different a section that already exists. Okay, so what I'm gonna do here is I'm going to fade out. I'm going to right a bunch of html and then we're going to fill in the missing pieces together. Okay, So I wrote a bunch of HTML in here, and we're going to fill in the missing pieces. But let's take a look at what this looks at right now. So when I refresh my page, I have an image here that has a link. You can see that at the bottom left there got a title, some text and an optional button. Although currently it's hard coded, so it's not optional. It's definitely there every single time. So we need to do some some work here. We need toe, basically remove all the to do's and remove this placeholder image. So let's just work down this one at a time, and let's look at each of the to do's here. So CC I 80 image and text section. I want us to be alignment. So this is going to be self dot image alignment and this is flex box. So this is going to be left or right, and it just matches with the CSS. Next, let's do the picture. I know that there's an eight rough up here, but let's see the picture first. So we want image self dot What did we call it? Not just image. That's an easy one. We want to make sure that it always fills exactly 786 pixels by 552 pixels as I am G and I am going to replace that with image dot TRL. Uh, the altar needs to be replaced. Even if there is no all text in there, it's always good to at least have the old attributes in there. Let's go ahead and refresh this page. Something broke. Oh, isn't that weird? Invalid block tag on line 11 image. Did you forget to register? Load this Take. You're absolutely right. I did. Let's add wag tail images tags. And remember, every time we use the Wag Tail image tag, we always need to load. Wait till images underscore tags. Now let's refresh. Alright, alright, alright, that's looking not half bad. Okay, let's do the title text and the button. So the title is right here. We're going to dio self dot title. Presumably that's what I called it. Yep, title next one is going to be text. This one is optional. So because this is optional, if self dot text and if and in here, let's do self dot text look that the cleanest rockets to ever exist. No rocket has ever been this shiny. It's so shiny, it's almost frictionless. That's at a little to do in there. Now this one's going to be a little bit different. This is not going to be self dot link text. This is going to self doubt. Link dot link text link, text. There we go. And the reason for that is because link is equal to link. Well, this is another class, so you can sort of think of it as link is equal to, and then it's going to sort of go into like a subsection of itself and its got link text is equal to and sort of. If you think about it like this, it's link thought link text is equal to, and now we need to add our links in years. So let's find all of these. There should be a few of them in there. Yes, two of them. So this is always going to be so dot link dot u R l. And we're getting that one from our struck value. So we've got a link that we're using here link text, internal page, external link and is using a link value or a struck value. And it gives us the dot u R L value. So this is custom stream field logic. This doesn't actually exist in the stream field, but as soon as the stream field is being rendered onto the page, it does exist. So we have access to link that you are l Now, Lastly, we should make those links optional. And the reason we're going to make those optional is because we have internal page an external link, and they're both not required. They're both optional, so there is a way for someone to just ignore both of those fields and there won't actually be a link. So let's just go ahead and solve that problem right now with if self dot linked that you were gonna make sure that that exists and if and we want to do the same thing with our closing tag and if and that's clean that up, there's one more. Where are you? Right right there. You have to link exists. Show the link. And if let's go ahead, all right, so that is now going to link to our shining service. So on our home page, we've now got a banner and we've got a stream field here that says the cleanest rockets to ever exist with the link that goes to more details. And when we click it, it goes to our shining service. And that is how we create yet another stream field, a struck block stream field with an image chooser image alignment, which is technically a choice block title text. And we're already reusing the link components in two sections. So we're really making use of that 24. Radio Block: Oakley Doak. Early in her last video, we created image alignment with the choice block. Now, a choice block can be great if you have multiple choices. But if you only have two choices, sometimes you just want, like a radio select option where it's like you in select one or the other with little boxes . So in this video, I'm gonna show you how to do that. We're going to open up our block. Stop I. And in the last video we created, image alignment is equal to block starts Chooser block. We're actually going to replace that with more of, ah, radio chooser block. So I'm gonna write the code first. Then we're going to do some imports and then we are going to replace this. So let's go ahead and write class radio chooser block. I guess it's not really a chooser like a page chooser or I am in shoes. Or maybe let's not call that. Let's call it a radio select block blocks dot choice block and you can notice that this is the same as this, and now we're actually inheriting this directly. Now, if we wanted to, we could go on dissect this source code. We could right click and go to jump to definition. Or you can view the source code on Get Hub at get hub dot com such wag tail slash wag tail And in here we went. Choice is a little bit of ah in it in here, a bunch of stuff going on. Get culpable choices and you could spend little time in here. I just sort of going through the source code is actually not that long if you get rid of all of the dock strings and the comments. But if you wanted to, you could always explore that. I always encourage you to look at the source code, even if it doesn't make sense at first. It's good to just read over it, because every now and then something clicks. Let's do definite. We saw that function. So let's pass in the arts and the quarks. Let's run super on this so it already runs in it from Choice block. And let's also pass in any sort of our eggs and choirs that are going to be passed into there are in tow. This one. Ah, and then let's set a field widget if I remember this correctly. It is self dot field that widget is equal to forms. So this is a jangle form who were going to import this in just a second. I believe it's a radio select. And then we can say the choices are going to be whatever the self done field, that widget that choices are. Now if you don't understand all of us because maybe you're new to Django or maybe you don't know any jangle it all. Then honestly, don't feel like you have to go and learn all of this. And if you like this feature, don't forget you can just steal it, right For my source code, it's available to you. Now what we're going to do is import this. I'm also gonna fix up some of my type was there. You saw nothing. There were no typos. Right now I'm gonna scroll all the way up to my page and I'm going to do from Django import forms and I'm gonna go back down until where the forms are. And where are way instead of blocks? Start choice block. All I'm going to do is replace it with this text here. Radio Select Block Let's go ahead and change that to a radio select block. It is, at its core, a choice block. All we said was used a different field widget and so everything else that we're applying default, help, text and choices. They're all going to be applied already. Anyways, those are the key word are eggs that were passing into in it, which is running super, which is going to run super on block stock Choice block. So everything is still getting past upstream. Let's go ahead and save this. Quarks is not defined. Yes, because it's not choirs, its core gigs that looks healthier. Let's go ahead and refresh our page. And would you look at that image alignment image, the left image to the right image left image to the right, and nothing is going to change in your template. All we did was swap out the widgets that wait till was using for a radio select. So instead of a drop down, we said, use a radio select. If you like this kind of thing, definitely feel free to use it. If you think this is just totally useless in sort of a waste of your time as a developer, ignore it and let's move on to the next lesson 25. Call to Action Block: Let's look at creating another new stream field, a call to action stream field. So let's say someone's on our page and we're trying to sell them on our rocket maintenance services, and we want them to contact us. Well, currently, we scroll down the page. There's no real way for them to contact us. Eventually, we can create a contact patient. We will. We will definitely get there. But at this point in time, we just want to create some sort of call to Action Block. Now, this is just going to be a very basic title and link and literally nothing else. So let's go ahead and add that block. Now I'm going to I'm gonna close all this stuff and pretend that I'm starting from scratch . Let's go ahead. Open blocks slash. Nope. That's wrong. Let's go ahead and open streams slash block. Stop I And at the very bottom of my page, I'm going to add class. Call to action block, block start struck block and I'm just make some room to work there. Okay, so I said, there's going to be a title that's going to be something and a link, and that's going to be something. Well, we know the link is already set, so cool that's done. And the title is simply going to be a char block, and we're gonna give us a max length of maybe 200 so it can be fairly long. If we wanted it to be and help Texas going to say max length of 200 characters, let's go ahead and add Our meta class template is going to be streams slash Well, let's do this one a little differently. It's going to be looking for a call to action blocked out html. And in fact, let's just see what it's going to be looking for. Just as proof. Let's not give it a template. I am going to give it like an icon, though, and then I'm going to give it a label called Call to Action. Actually, now this is great. This does nothing, though, because we need to go over to our homepage and enable this So in her body, we just have to type. I don't know. C t a block start call to Action Block. And remember this Texas actually not being used by us right now, there is a way to loop through every single stream field and detect which stream feel it is , but we're not going to be doing that. Let's check out the terminal Terminal says There are no issues. That's good news. Let's go ahead and edit our home page and we should see a call to action blocking here. But Babu, there it is called Action, and the action is just going to be something in the long lines of the best dang rockets ever cause. I don't know. I guess we're making rockets now. Probably not. I'll change. I content a little bit later. Uh, let's I want to do this shining. No, let's not do shining. Let's do cleaning. And let's change us from the best dang rockets ever to actually something a little more applicable here. Was he a rocket in space recently? Get it cleaned. Clean your rocket now can imagine they get pretty dirty with all that fuel being being used and, uh, you know, entering and exiting the atmosphere and all that. All right, that's saved. Let's refresh your page. And would you look at that? It's actually not saying that the template doesn't exist. It's just rendering it straight to the page. No, that's cool and all. But we don't want that. We want this to actually have a proper stream field. We're going to say it has to live at called Action. Blocked out html. And when I refresh this page now, we're going to see that simply does not exist. Area We've seen this. Oh, my goodness. 100 times by now, let's go ahead and make that exist. Rocket Man Templates, streams and new file call to action block dot html. All right, so I just added my call to action Block in there, and you can actually see what I did. There s so there's a title and there's going to be a link. Actually, don't think that Link is quite right. Actually, that's supposed to be class. That looks better. Okay, now we just need to add our title again. This is coming from a stream field. So a stream field weaken yourself dot title or value dot title. Let's go ahead and do self dot title not to be confused with a page title. And because this one is a link that comes from called action, So self dot link link is going to give us link text, internal page, external link and through customs stream field logic You are l so in here we can dio self dot link. Don't you are? No, that's the wrong one. Link text. And for the girl, this is where we were doing self dot Link that you are Huzzah! Was your rocket and space recently? Get it cleaned. Clean your rocket now and it brings us to our rocket cleaning service page beauty. Now, this has been fun, but, uh, I think it's time to start getting into a few different types of stream fields and also playing with things like a snippet chooser block where we can actually select a testimonial . 26. Testimonials and Snippets: Once upon a time, many, many videos ago, we created this testimonial section. We had it quotes from Yoda and Chewbacca, and then we also registered are testimonial as a snippet, and this testimony was actually a jangle model. It has nothing to do with Wag Tail, except for the fact that we we registered it in the Wag Tell Edmon and registered it as a sniff it. Now snippets are nice because they are pieces of data such as this that we can select using a gooey graphic user interface. And in this video, that is exactly what we're going to do. We're going to allow people to select a testimonial and create a testimonial stream field. But instead of having to write the stream field over and over again because maybe the same , the same testimonial is going to be used on four different pages. We're simply going to say, Select one of these, and when we change it on one page, it will change it on every page. And let's just go straight to our home page home models that pie. And in here we are going to directly at a new stream field, and this one's going to be called a testimonial because we're going to be using a stream field that is just one particular type. For instance, we have this title block. It's a struck block and it only has the one field. So why are we using blocks that struck block when you know we could use blocks dot char block instead? Now, if we have more than one field, like in the card or our image and text block, even in our call to action Block? Sure, it makes sense to use a struck block, but when you only have the one field, it's sometimes easier to just not to use a struck block it all and use what Wait till gives us out of the box. So what we're going to do here is we're going to say our next dream field is called testimonial, and we're going to use a snippet chooser block, which does not come from our blocks file and at the top of our page here. We want to do from wag tail dot snippets dot blocks, import snippet, user block. And if we look at our terminal, you can see clearly it's complaining. We have one positional argument. Missing target model. It does not know which snippet to select. Now, even though our site really only has the one in here, we still need to tell which model to select. Now that's actually really easy to specify. Here we can simply say, right inside of the block, pass it in directly. We can say Target model is going to be. Which model do we want to use? Well, in a string, So we'll sort of lazy load this testimony eels. Can I spell array testimonials? Don't test Moughniyah Will. I have no idea if I spell that right. My terminal will know, though that looks OK. Let's go ahead and thats editor homepage Home page on page home page That scroll to the very bottom we have a new one in here called Testimonial automatically gives us the name and that name is simply coming from here. Let's make that just a tad smaller. That name is coming from here, so it is actually being used at this point because we're not giving it any sort of extra labeling. The icon is the snippet icon, just a couple of leaves that comes default and now it's going to tell us to choose a testimonial, and at this point, let's choose classic Chewbacca. Let's go ahead and publish, and when we load up our page, it's not quite going to be what we expect. We're gonna scroll down here and cool. We see the testimonial, but we can't actually make too much use of it at this point. So let's go ahead and pass us a custom template as well. And at this point, I think it's easier if we just put this on separate lines. So the first parameter, the first positional parameter keyword argument, rather is the target model. The 2nd 1 we're going to give it is a template, and this, at this point is no different. Then passing a template in the class meta. So we're simply going to say this temporary needs to live in streams slash yes, Imo nial block dot html And now when we refresh our page, we're going to see when it loads. By the way, if your side ever loads really slow like that and you're getting impatient with it, it's this thing right here, you Django debug tool bar. This is a profiling tool, so it's going to log all the queries, all the static files, everything in there. If that's taking too long, you can just go into your deft up high settings and disable it. So we're seeing the dreaded template Does not exist error. He's actually not dreaded. We know exactly what it's doing. Is trying to look for a testimonial block in all sorts of different places. Let's make it exist. So in our streams, let's go ahead and add a testimonial block dot html. And at this point, it's sort of difficult to understand what we have access to because inner c t a block We had a title, so it was self doubt title or in our text block. I believe it was just text. Yes, so inner in our template, we would have just said self dot text or value dot text. But what do we do in a testimonial block or a snippet chooser bloc? Rather when we don't know what's being given, So what we can do is start to experiment. First thing we can do is just pass in self. Let's see what this returns. You can see that it says order by Chewbacca. Well, that's not the testimonial. The testimonial is G R R R R and the attribution is Chewbacca. So if we open up testimonials, the models file, we'll actually see that we've got a quote here and the attribution. We also have underscore underscore str underscore underscore. So dunder stringed under And that's what it's returning is returning the string representation of itself. We don't want that. The nice thing about that is whenever you see an object returning the string representation of itself, that's because it's the entire object that's being returned. It just doesn't know if you want to see the quote or the attribution Fielder or what do you want to see? So it just says, Okay, well, I'm going to give you the name that you gave it And that's why. And that's exactly why we set this earlier was, uh for helping, but also makes it look a little nicer. So let's go ahead and try itself DOT quote. And when we refresh your page, there it is. There's are quote and attribution, and once more, I'll refresh your page and it will say Chewbacca just like that. All right, so I didn't want you to watch me, right? All this boring HTML test the fact that I made a couple typos, my CSS that didn't quite work out the right way the first time. So I faded out, faded in, and we're going to fill these in together. So let's take a look at what this now looks like with just the to do text in there. We've got a quote that we need to fill in and attribution that we need to fill in. So now that we know how to access this weaken, simply do self dot quote and down here we can do self dot attribution and again, I'm getting these directly from the snippet itself because we have a testimonial here. It's a model. It's a jangle model, but it's registered as a snippet and we didn't use a struck block. We said, Just select that piece of of content. Select that testimonial. Just that raw testimonial. This testimonial model now becomes quote unquote self or the value. So let's check this out. Oh, yeah, that's looking pretty good. Okay, We have one more thing here because I really want this. What's at an em dash? Start the long one. I believe That's a long one. Yeah, it is. That's Ah little dash there. And now we have a snippet chooser block, and at any point in time, we can changes testimonials. Let's do two examples here. Let's go and edit this testimonial. What else is to Bacchus say? Ah, he shoots a gun So it says P p p u you be you refresh. Notice how I didn't change the page. I changed the testimonial data. I did not change the page at all. And when I go back down to the page here, well, this is the old one because I loaded the pages roughly at the same time. If I choose another testimonial look at that. I can select, give people hope you by Ibaka or do or do not. There is no try Biota. So I mean, it's like that one publish. Refresh the page and this will change as well. Huzzah! Now we have two different ways to change data in our website. The nice thing about this is, if you wanted to, you could add this snippet chooser block to your flex page to your service page to a future blogged page of yours. And if you were to, I don't know accidentally make a typo in here. You wouldn't have to go and change out on four different pages. You could literally just go in here. Change it once, and it will change it across your entire site for you. And there we have it. We are now using a wag tail stream field to select jangle model data. That is pretty cool stuff. 27. Table StreamField: Let's take a look at table based stream fields. Now. If you come from a front end sort of developer role, you're gonna think, Why would we ever use tables? Tables are obsolete. Tables are the worst thing ever invented. There's literally nothing worse than a table when it comes to the Internet. And while most the time I would agree with you, there are some cases where you need to use the table. Sometimes you just want a table. I don't know. There are reasons for them. And ah, we are going to cover that in this course. So first things first we need to enable these, and I'm actually just going to reference the docks because I don't do this one too often. So I don't remember it off the top of my head, which is also great because it gets to show you that even people on the core team reference the docks. So I actually have this up already. Ah, here's the table lock and is going to create something like this. We'll get into that in a little bit. Ah, the biggest thing we have to do, though, is take the wait till contract table underscore block app and enable it. So let's open up our base by base based on pie and throw that in there. And I just put that with the other contracts. And then we can close that now to keep this somewhat presentable looking so that the code doesn't just look like a giant mess. I am going to sort of go about adding a table block in a roundabout way. But the shortcut that we took in the last video you can absolutely use as well. And if you don't recall what the shortcut was, definitely watched the video on the snippet testimonial block because we added a snippet without doing anything in our block. Stop, I So what I'm going to do here is I need to do an import. Actually, the 1st 1 here from wait till the contract table, block dot blocks import table block. Okay, that's throw that at the top of our page and go all the way back down to the bottom. I'm going to take a class. What do we want to call this? A pricing block, Maybe a pricing table block, I suppose. Let's say there is a table block here doctoring of I don't know, pricing table block. I don't know if you're going to use a pricing table block or not. But, hey, you know what? Let's add one anyways. That's at a class meta template is equal to. So we know which template to you is pricing table block dot html And I'm actually going to disable that just for the time being. The label is going to be a pricing table, I guess, because that's what we're calling it. Icahn is going to be a table table and help text will be. You were pricing tables should always have four columns or some other advice like that. So no, we can grab this. Go to our home models that pie by grabbing this cause you can't see that. I'm pointing with my eyes but pricing table block and we're going to add it to our home page pricing table. Sure, and let's add block start pricing table block and that is it Checker Terminal. There are no complaints. That's good news, and thats editor home page Now we probably don't want to pricing block on our home page pricing table, but should we ever wanted, we could put it there. We could put it on any other page as well, which we're going to do in a later video. So I just selected our table, and by default we yet is the Roa Header is the column header. So, like, should these be bold? And should these be bold? And the table caption is mostly for screen readers. It helps screen readers with accessibility so that people who have a hard time seeing what's on your website understand what the table is about. It just gives those devices a little more context. It's really helpful. So let's say rocket package pricing table. That's what we're gonna call this. And I want the first row here to be a header. So it should. Hopefully, people, if it's not, will make it bold. And we're going to have ah standard standard package, standard package and a premium package. We've rocket cleaning standard package is gonna have a check box. Let's go ahead and add a check box. That guy right there and also comes with premium. But what else? Rocket cleaning rocket shining is a premium package only. Oh, no, I'm out arose. Okay, right click Insert row below rocket fueling. If you get our premium package well, even fuel your rocket for you. Ah, answer. One more row below rocket painting. We will repaint your rocket in case it lost some paint when it was entering the atmosphere . That's good enough for now. Let's go ahead and publish this click view Life going all the way down and we can see that this is a table of sorts. And sure enough, in the source code, it is, in fact, a table. Now what we need to do is we need to go back, enable our template. Now, the problem with this is we don't know where to get this source code from. So what are we possibly going to put in this template? Well, we have really only one way to do this. We need to get to that code from the source code. So I am going to right click on my table blocking, go to definition. And I'm just gonna skin in here for where is the template? It's got to be in here somewhere. There it is. At the very bottom. It's already setting a template table block, blocks table dot html. Now, can I open that in here. I cannot open that in here because I don't have a quick little jump like that. So here's what I'm going to dio get hub dot com slash way tell slash wag tail and I am going to look for table block blocks table dot html. So it's just going here tabo dot html When searching this repository and this looks right, Wait till Con Trib table block. We did import that Templates. Table block, blocks table dot HTM own. There it is. And here is the source code that we can use. So what I'm going to do is copy this whole thing into my new template. We can close this one into my new templates called Pricing Table Block. I haven't created that yet, though, so let's go ahead and create that in the Streams folder. Let's do praising table block dot html and throw that in there. We could even had a fake little title. Just we know that this is custom refresher page. Sure enough, this is custom. Now, at this point in time, you can go through this and you can say that the table head should in fact, have I don't know a minimum with of 300 pixels or something like that. You can also give the table class. They're the table elements, a table class of table, responsive. Just play block things like that and you can customize this to be exactly what you need it to be. Now I'm just going to copy what I have pre made for this one. There's not really a lot of customization in this. To be totally honest, I've really just made this look a little nicer with bootstrap classes so you can see that Ah, my codes a little bit different. I actually did at a minimum with of 300 pixels for the first column and for every call for every other column, there's a minimum width of 170 pixels. Now, let's go ahead and refresh this page and see what we get. We can see at the bottom here. Ah, I actually did an interesting thing here where I want actually pricing at the bottom, but that may not be what you want. So I'm gonna look for that. And I just happen to know that I've added this in here is Footer. I don't want that. Let's get rid of it. That's just trying to make this column in this column Bold at the very bottom. We don't want that. That looks better. And now we can actually go into responsive mode here. Where is that I got there? It is responsive mode due to do to do that. A card. We've got a table here that goes all the way across. So look at that responsive table. Now, the next thing to note is that we can add custom settings. So we've got a default configuration, but we can add some extra stuff. We can change the default in here if we wanted Teoh. So minimum spare. Rose, How many rows you start with? How many columns do you start? With things like that. I am not necessarily going to apply anything custom, but I'm gonna show you how to do this. I'm gonna grab all of this from the docks. And where am I? In the home page. I'm going to want to pass table. Options is equal to new table options now. New table options does not exist yet. We're going to make this variable exist in just a moment. Something to scroll up here, create a new one called New Table Options is equal to, and it's really just a dictionary. The minimum Spare Rosa zero. I don't know. Starting Rose is gonna be four starting columns going before so, instead of a three by three, will have ah, four by four column headers. Yes, we have column headers, row headers. Actually, it's too. Column headers. False, but Row Headers, True Context Menu. We can change a bunch of this stuff in here as well. Editor text. That's the one we want. I'll scared of language. The height, renderers. There's all sorts of stuff in here. We would actually be in this video together for quite a while if we went through all of the different options. But definitely check out the docks and also check out the I think it's called Hansen Table . Yeah, Hansen Table 6.2. Here there are different settings that you can check out in the documentation as well, but let's go ahead and see what my new settings look like, so we're not going to see in here because this table is already set. But let's go ahead and add a new pricing table. Look at that. We've got a four by four grid inside of a three by three grid. So there's some customize ability in there that weekend. Weaken Dio. Really nice thing about this is if you have a client, whoever wants to just, like, display stuff in a table. But it's already in a spreadsheet you could just copy. Like from Google Sheets, I believe. Works pretty well. You could just copy all the cells and literally just paste it in there. And they just work really nice. But I'm gonna delete that one, and I'm going to save the page and we will be done with custom tables. So that is how we make tables and sort of work with changing the defaults table options. 28. RichText StreamField: Let's now add our first stream field to our flex page. After all, we've been adding all of these stream fields to her home page, and we're making it very flexible, which is nice. But it's not really the purpose of the home page purpose of the Flex Pages to be super flexible. So let's just go ahead, close up our code here and go to flex models dot pie and we can see Yeah, there's nothing in here, not a single custom field. Now, if I split my screen here and over here I go to home models dot pie, we can see oh, search of stuff. Let's get rid of that. And we basically want from our home page, this entire body section so we can literally just copy this over. Or if you want the practice, you can write it by hand. That's totally cool. It's actually probably a better way of doing it. And so now we're Flex Page has this body is equal to stream fielded, have title cards, image and text called the action, a testimonial snippet, chooser block and a pricing table. Now that's great, but the body is not going to show up at all. We actually need the panels. The panels are important. These content panels actually allow us to expose the body field. Because again, Wag Tail and Django, they make no assumptions about what kind of data we want to actually have accessible to our client or two. The content entry people. So let's do content panels is equal to content panels. Plus, and then we want to add a stream field panel and at her body. Now, if we're to save this page and we Yep, sure enough, we check out our terminal. It's gonna say Stream Field is not defined. Basically, this file says, What's a stream field? So we go up here and we do from rightto dot core dot fields import a stream field, and that's also do from Quito dot admin dot edit handlers import a stream field panel. Let's go ahead and save that. Blocks is not defined. Well, that makes sense. We have all sorts of blocks in here. We're using our own custom. Blocks were also using a snippet chooser block, so we need to import those as well. That's import our block, so it's do from streams, import blocks and Let's also import the stupid chooser panel from way tail dot snippets dot blocks import import snippet, chooser block. What else are we missing? New table options is not defined because new table options is on the home page. We could import that as well if we wanted to. We could do from home dot mottoes import new table options. And there we go. Now, if we actually go in, edits one of these pages. Sure enough. Yeah, we saw that air. We're going to see it again, as expected. Wait Till is now looking up our body field, all these stream fields and doesn't exist. So now I'm gonna cancel my server, and I'm going to make migrations. But I'm gonna run it just on one particular application. So I'm gonna do python managed up. I make migrations. And it's the flex app that I'm working on. Hadfield bodied Flex page That's looking good. Python managed a pie migrate run server again And when we refresh, this will go away. Uh, no. We have title standard cards, image and tax called Action Pricing table. And all of these were going to work the exact same way we set up on the home page. So within just a few minutes, we are able to completely customize our flex page, and it will just work. We did the work once on our home slash models a que our our home page here we did the work once. Why would we want to do it again and again? And that's exactly what we're doing here is we tend to work once we're just going to leverage that work again. Now that's cool, but we want to be adding rich text. So let's go ahead and add some custom rich text here. We could get rid of this, and I'm gonna make a bigger again. At this point, rich text can be imported two different ways and I'll show you both ways. The first way is a very, very simple way. If you're just using just rich text and you don't need like, a special title field or like text alignment options or anything custom and you just really want a whiz e wig editor, this first way is definitely the way to go. So we're going to do from wag tail dot core import blocks, But we also have block, so you want to rename one of these. It doesn't really matter which one we rename, as long as we rename it consistently. So, like if we renamed our blocks, we have to rename this one and that one and that one and that one or an easier way because there's less of these weaken rename the wagged l one. So wag tail blocks is what I'm gonna call this. So import blocks as well. Tell blocks we can also put this on separate lines to make this, you know, a little nicer. Look at Let's go ahead and add Rich tax. Just a standard rich text field. We're gonna do a new stream field. It's the first value is going to be rich text again. It doesn't have to be rich text. That's just what I'm going to call it. And now I'm going to give this a rich text block. Save that and let's go ahead and refresh this page and we will see that I will now have an option for a complete with the wig. Look at this. It comes with bold italics. H two h three h four ordered list a Nord A list horizontal rules. You can even embed videos you can link to pretty much anything an internal link, external link, email link, phone link or an anchor. Lengthy anchor link is new to 2.7 documents, and you can even add an image in here. If you wanted to, I'd say this one's going to be aligned to the left. Now, Now, this is really, really cool because I can say this is an H three turned this into an H three put horizontal rule in here and I can say this, but none of her stream fields are going to show up. And again, that is, I actually forgot about that one. Been a little while since we worked on that. So it's open up our home page and remember this thing. Yeah, we just need that eso I'm literally just going to copy that. Open up my flex page, come down here and paste it. And so all we're saying is in the based on tht mail, there's a block called content. Whatever is in there, let's override it. Let's inject our stream fields into this now. This is going to work, not the way you would expect because include block doesn't exist. So we need to load wag tail core tags. Let us refresh our page. Block content appears more than once. Yes, because that is not a banner. For whatever reason, I thought that was a banner. Let's get rid of this. Let's get rid of that. We can only have one block content per page. How are Look at that. We got some rich Texan here. This will show up as and h three. Sure enough, there it is. This is regular paragraph. Sure enough, there it is. But what if we wanted to put this into its own template? Cause right now, this doesn't really fit in with the rest of what we're building. So let's go ahead. And let's tell this to use its own template template is equal to streams. And let's call this a simple, rich text block. And it's going to be called simple because eventually we're actually going to limit what features you can add in here. So we're going to get rid of the titles because there's already a title. We're gonna do a bunch of other stuff to it now that template does not exist. Let let's make it exist so simple. Rich text block dot html and that's going to go in these streams folder. And so I just wrote some HTML here. And when we review this page rich detectors, the rich text is going to go here. But, like, what do we put here? Well, let's go ahead and put self. Sure enough, we could do that. We've done it before now that's pretty great. Let's go ahead and edit this once more and let's put a link in there just just to really drive this home. Let's just grab a link and let's link Teoh. I doesn't really matter the about us page. It's just sample content and we just want that linked to show up. And sure enough, our link does show up as expected. Now, what if because while I'm editing this, what if I don't want images in here? What if I don't want people to link to documents and I don't want h 23 or four in there? Well, with a rich text block, we have this cool little keyword argument called features, and we can tell what features we want. So we've got bold italic, so let's type bold metallic Oh, and I just thought of a good joke that I am totally putting in here. My favorite font as an actor. Italic bold win. Ha! OK. Ah, we don't want h 23 or four, but we do want both the lists. So let's go ahead and put the list in here. Even ordered list. Even a Norden list. I don't want the horizontal rule. Get rid of that. I don't want anything else except links. So let's go ahead and put Link and let's refresh your page and let's see these options change up here. And would you look at that bold, italic ordered list on ordered list and a link? Plus, you know, the line break that always comes with a busy week can't really get rid of that. Undo and redo. And if it any point in time, you are thinking, Hey, Caleb, I mean, that's cool that you can limit features, but where did you get these from? How do you know to put bold or italic or oh al or you l or Link or anything like that in there? The docks. That's my That's my totally not awesome answer. I was just the docks. Eso if you go into the docks and you just look at customizing the Where are we? Hear advanced topics Customizing Wag Tail Customizing the editing interface. You can actually see that the feature identifiers provided on a default waittil insulation are as follows. H 12345 and six So you can actually enable H one, 45 and six. Bold italic. Oh well ul horizontal rule link a document link image and embed. There's also a few additional identifiers as well. They're not enabled by default, but you can enable them in your list so we could also put code superscript sub script strike and block quote in there as well. So that's option number one. Option number two is if you need toe use rich text in a regular stream field with some other option. Let's go ahead and add that so we can create essentially a new one in here. So rich text with I don't know that title on this. Give us a link. Two blocks done rich text with title block, and this currently does not exist. But if we open up streams dot blocks, we can add it in here class, whatever that was called that very long name and blocks dot struck block, and we can add a simple title like we have been doing before. Title is Eagle two blocks Tut HR block max length of maybe 50 or something like that. And then we could do the text or the It's not called text. What's called Content is equal. Do blocks dot a rich text block rich text block, and then we could give it features as well. So let's go give our page a quick little refresh here that'll get rid of this one for us because it's no longer available. We've got rich text with title. Give it a title. We've got Rich Texan here. We can limit these as well Features is equal to If we don't want any features, let's just give it an empty list. Lastly, let's give it a quick little template. Your template is equal to streams slash I'm going to reuse the simple rich text block dot html, and I'm gonna feel this with a title. Anything in here because I need a title hands through some warm ipsum in there and I must save a draft. So I can get a fresher version of this page. We can see now that all of this has been disabled. It's literally just paragraphs at this point. Paragraph, paragraph, paragraph, paragraph publish and let's refresh the page. So now we're going to see something that we haven't actually run into yet. We are seeing self as struck value with all this other stuff in it. Now the reason this is showing up is because in our template, we're simply saying self were saying Return the entire stream field itself when instead maybe we want a title. So self dot title and we want the content, self doubt, content and I didn't call it content. I called it context because that was a typo. But I'm gonna call it context in here just, you know, go with it. So we now have some context in there. Now, if at any point in time you ever see your page showing HTML or stuff that looks like HTML, you were going to need to use the rich text filter on it, so you load wayto core tags and you simply dio pipe rich text. Now this is already escaped and looks totally fine or un escaped rather. But sometimes when you're doing it this way, you might actually see, like, a link in here the actual HTML link or something that looks very similar to it. And you want that to be HTML. So you adjust to use the rich text filter, and that will solve your problem. No, I'm not going to keep any of this stuff in the source code. I'm gonna undo that. Close it, and I'm going to get rid of that. And I am going to get rid of this one because I'm going to stick with the original way we did it. And that is two different ways on how we can add a rich text block to our flex page stream fields. 29. Image Block StreamField: just like our last lesson. We are going to create a brand new stream field directly from a wag tail component without writing our own struck block. What I mean by that is if we open up flex models DuPuy from the last couple of lessons for roughly the last couple lessons, we used a snippet chooser block directly from wait till without using our stream slash block stop, I file in the last lesson, we added our own rich text. We gave it a template and limited the features and we didn't to use the block stop I file at all. Well, we did once and then I undid all that that work because we didn't want to keep that in this video. In this lesson on our flex page, I am on our flex page here. We're going to add a simple image chooser, and this is going to be a larger image. So let's call this large image and we're simply going to say from wag tail blocks. And if you're just tuning and now wait till blocks comes from from wagged Elle decor import blocks and then I renamed it to wait tell blocks Wait, Tell blocks, image chooser, block and in here I'm gonna give this a little bit of help texts, just like we normally would in a regular stream field. Going to say this image will be corrupt to somewhere around 1200 by 7 75 And let's also specify a specific template. Our template. So let's put this in streams large image block dot html. Now when I go back to our about US page, I broke something has no attributes, image, shoes or block. And that was because I was honestly going a little too faster. It's not from wag tail blocks. What was I thinking with that one? Images are its own beast in Wag Tail. It doesn't come from wait till blocks It comes from wag tail dot images di blocks import image chooser block Pretty much the exact same thing that snippet chooser block does. But this one is imaged chooser block. So let's go ahead. Save that our terminal is now very happy with what I have written Refresher Pidge and I have a large image block in here so I can add any image in here. I'll just add that one published and sure enough, When I preview this page, we get a template. Does not exist. Error. I'm not even going to look. I know that that's gonna happen. I'm going to create a new file in the streams. Templates. Subdirectory. So rocketman templates, streams. New file in here called Large Image Blocked out. Html. Now I can do my own custom rendition in here. I don't have to let wag tail control everything. I can do whatever I want at this point, but the first thing I want to do is I want to load the wag tail images tags because I want to be able to use the image template tag. And the image itself is this entire object. Everything that is being passed into the context of this template is the stream field itself. So previously we've done self dot image in a struck block, but this time there's no struck block. It's just an image we're going to do. Image Phil 1200 by 775 at his image. And then I can simply say I am Gee, that you are Ah, I am g dot lt And yeah, there. Is that there? Yeah, it's people felt that wrong wait till image is plural. There we go. And look at that. It's Ah, fairly big image, isn't it? Now that doesn't quite line up with the rest of our site. Looks like something is a little bit off there, so I I'm going to add a little bit of template ing in here. And then Caleb do the fast forward thing again. All right? So I just added that in there and to do to do, to do. There we go. That's looking a little better. That feels like things are lining up, right? Oh, yeah, that's that's better. Now let's make this page actually look like a thing like an actual paid, You know, Let's have some text. So we've got rich text in here. Let's go ahead and add some lower, um, classical or um above classic Lauren below, and that is looking just a little bit squishy with the image. Let's go ahead and add margin on the Y access. There we go. That's looking pretty good. So, you know, basic about us page, and that's how we use a large image stream field directly. Now, if you're coming from that previous video and you're wondering how we do it any other way. Well, we actually have several examples of this already. In our blocks up I image chooser is somewhere in here. Image in text, block images equal to image chooser block so we can just look at the image and text block dot html template as a reference for how to do it this way. Otherwise, we can just use the image shoes or block directly, and we don't have to worry about sub costing a struck block in order to get an image chooser block. We can just use it directly, which is really, really nice. You can also do the same thing with we've seen this before. Sort of blocs dot hr block. You can do it with blocks dot text block or block stuff page chooser block, and I actually I would like to encourage you to try that out, create a stream field and I don't know, call it custom page. Maybe don't call it page itself. That's a word that's already used in the in the template context. You might run into a problem with that, but custom page wouldn't be a problem, and then you could use blocks I guess would be Weg toe block, stunt page, chooser block. And you could then work your way from there. So maybe go ahead, give that a shot, see how that turns out. And that will give you a think a pretty good idea of how you can sort of bypass using a struck block altogether if you keep your stream fields nice and simple. 30. Custom StreamField Validation: let's add custom stream field validation when it comes to our link block, and I will actually just pull that up when it comes to our link block that we have here. We have an internal page and an external link, and we've added a struck value right up here. The struck value to figure out which one supposed to come first and give us the UL property inside of our template. So basically, we've applied some additional logic to our link block. But because both of these fields are not required, they can both be empty and they can both be selected. And so, for your content entry people. When they're given two options, they can select an internal waittil page with a page chooser block. Or they can enter an external link, which is really any link to anywhere else on the Internet. They don't really know which one's going to come first all the time. You can give them help text on both of these fields, and yes, that's very helpful. But it's even more helpful If you Congar I'd them programmatically. Now what I mean by that is, if we added our home page, there should be a link in here somewhere. Sure enough, it's our very 1st 1 We've got a Lincoln here with link text Clean your rocket now internal page. And there is no external link. But I can if I wanted to provide an external link. Wait till dot Io And this allows me to save this Totally fine. There's no problems with this now. That in itself is the problem is there are no errors because when I come back and actually preview the page, clean your rocket Now you can see at the bottom left that is still brings me to the cleaning services page. However, I also added the way till dot io link in there. And when you're working with your clients, you want to make content entry the easiest thing for them cause honestly, maintaining ah website with good content can be quite hard and so a good way around this is to simply solve this problem by giving them some form of errors and so we're going to give them validation. Now, the first thing we want to do and I'm gonna try to do this somewhat, all in one view here, So I'm going to do from django dot core dot exceptions import validation air and I'm also going to do from django dot core No, not DOC or that was habit dot forms dot you tills import ever list. And whenever this link is saved, we can run a particular method on here called Clean. So we can say Death Clean is going to take itself and a value. And let's say there are currently no errors. This is going to be a dictionary of errors, but currently there are none. And then we can say something along the lines of If you know, whatever fails, we don't know whatever is. We'll change that in just a moment. We can see errors is equal to whatever the field name is is equal to error list and then your error text here. And then we can say if there are, in fact errors in the dictionary. So if there are items in your dictionary, we can raise a validation error validation validation ever in your link and let's pass it some parameters. The Param zehr going to be the errors. Otherwise, if there are no other heirs, that's not going to execute and weaken simply run super that clean value now to quickly go over that just sort of as a quick summary whenever this gets saved, whenever a link get saved and this could happen multiple times. If you have multiple streams, fields using multiple links, this clean function is going to execute. And then we're saying for this particular cleaning, a sign errors to be an empty dictionary. And then if our validation passes or fails, depending on what we're trying to check, we can say errors. Whatever that field name is is now part of an error list. We imported air list and validation ever up here. Then we say, Hey, if there are errors because we can do multiple if statements in here, if there are errors, validation error is going to be thrown or is going to be raised, then we're going to say if there are errors, raise a validation air with some sort of text in there, and the parameters were going to be whatever that error dictionary is going to be. Otherwise, if there are no heirs, just keep doing your regular thing. That's what this means. Just keep chugging along, so let's go ahead and give this a quick little example? We can say if true, the field name is going to be, let's say, external link and the error text or the ever list is going to be not a really error, but it shows up anyway. Let's go ahead and save that. And there's no module name jango that form and that is actually correct. It is django dot forms plural. That looks better. Okay, so we're on our page. We have an external link and an Internal Inc We know that no matter what happens, this is going to run. And we're saying, if it's true, well, true is always true. So this is going to execute. We're going to then add external errors to our ever dictionary. It's a airless. This will then also be true, and it will raise a validation air. So let's go ahead and save our draft. And sure enough, this page could not be saved due to validation errors and check this out. Not a real air, but it shows up anyway. But Lincolns also using multiple places there. It is as well, and there it is again and one more. There it is again, and I know there's four validation errors because there's a little four up here now. That's actually not a very good example, because you don't want to run or raise a validation air every time someone saves a page, because then they won't ever be able to save their page. So let's go ahead and get some of the values from this whole block that's being saved so we can say the internal page internal page is going to be value. Don't get Internal page, and the external link is going to be the external link. And so at this point, we have two variables that we can use. We can say if Internal page and external link let's go ahead and raise two errors, one for internal page one for external link. And let's say both of these fields cannot be filled him. Please select or enter only one option. Something like that. All right, I'm gonna save that and let's go ahead, save a draft. We could just as easily publish a page as well, and this time we only have two validation airs. This is the one that were mostly working with, cause it's near the top of the page is most convenient to look at, but we can see that both of these fields cannot be filled. Please select or enter only one option, and it says it for both of those. Now that's fantastic. Let's do the opposite. Let's do Elliff, not internal page and not external link. We want different. There's and we want this error to say something along lines of you need just like at least one of these. So please select a page or enter a you Earl for one of these options. That's Checker Terminal Terminal. Life looks good and I'm going to get rid of that one, and I'm going to get rid of that one, and we'll see a different era this time. So just a show you that we can publish it instead of just saving a draft to get validation errors. Sure enough, here it is. Police. Select a page or enter a your AL. For one of these options, I am going to select the about US page. No, I know there's one more on here, so it's gonna throw me another validation air. However, this one past the about US one totally passed. That's not a problem. Where else are we looking? We've got one in here as well said. There always needs to be a link. We'll put that one as shining to do to do. Where is the other validation air? Is that it? No. Yet because there's two there. Let's click publish. Our page has been published. Look at that. They're page past our own validations. So now we know that this will always be an internal link or an external link. On top of that, we have additional logic already for our link value here, saying that if there is an internal page, use that one or if there is an external link, use that one and there will only ever be one of these. There will either be one or the other, but never both. And lastly, a little bit of cleanup. Here, Let's go up to the top of the page. Cool beans. Put those up there. Life looks good, and that is how we add custom stream field validation 31. Limiting Pages: up into this point, we've created a number of different pages, and we actually have one particular problem that we haven't really addressed yet. As our website grows, it can really start to get out of control. For instance, we can add a child page to our home page called Home. Now, why would we have to home pages on our website? We have a dedicated page just for home page. We probably don't need a second home page. There are some instances where you probably want that, but in this case, we don't on our home page. We already have a service listing page, and we are able to create another service listing page. We only want one of those, so that's a problem under our route. If you click that little icon up there, that globe and you say Add Child page, we can now say that any other page type except the home page because it's already available . But we can say that service listing page, the service page or the Flex miscellaneous page can also be ah, home page page type. We don't want that, so we need to restrict these. Luckily Wag Tail gives us the ability to do that very easily. So let's go ahead and start by restricting route. What child pages can route have now? We don't have access to the root page itself That's in the way tail package. But if I open up home models dot pie and I specify the apparent page types, I can give this a list of AP name with the model name APP named dot model, name, model name. There we go. Third Time's a charm, and this will say that the home page always have to have a parent of whatever app dot model name is now. This doesn't exist, but wag Tail core dot Page does exist. This is our route page so we can save this and let's let's go back here under our home page when we click. Add Child page Home page is no longer available home page. Can Onley ever live under our route page? So it's still here but is not allowed to live anywhere else. So now we're restricting how our websites created, so every website starts off with a root page. Then we've got a home page and we're saying there cannot be another homepage or If we go into our page, we cannot list a home page underneath our about US page. That option used to exist. It no longer does, which is really nice. So we're starting to limit some of this. Let's go back to a root page and let's get rid of these other options. Let's get rid of Flex Page Service, listing Page and the service page so none of these can live under the route page. Now. How we would go about doing this is if we open up are flex models dot pie. We can specify the apparent page types here so lets a parent page types is equal to, and this is just a list so the flex page can always live underneath our home page. That's okay. We already have that set up. That's where our about Page currently lives. But we also want flex pages to be Nesta vel underneath other flex pages so that you can create and about us and then maybe a team page and then maybe a dedicated page for each person at your company. So we're going to say the APP name is Flex. That's the current one we're we're in right now and flex page. So we're really just saying parent page types. You're allowed to be your own parent. Sounds a little bit weird, and the home page is allowed to be your parent. So let's say that, and I'm going to go just back a page here. Sure enough, this will allow me to create a flex page here. That's fine. I'm gonna go to root. I'm no longer allowed to create a flex page. So that's perfect. And just really make sure that this is working. Let's go and actually check out our flex page here. Let's add a child page, and sure enough, it's still allowed to be there. We want to eventually get rid of the other two, and there's a second page property that we can assign to get rid of that or get rid of the other two. Now we've got two more in a route that we don't want. We don't the service listing page or the service page, so let's go ahead and open up services models up high and her service listing page. That's what the parent page type this can only ever live underneath. The home page home dot home page again. That's the APP name, and this is the model name. And if we scroll down to our service page here, we could do the same thing parent page type. But this time we want the parent page type to always be the listing page. So service pages always, always, always, always have to live underneath our service listing page here. So we say services dot service listing page And again, that's just the app we're currently in it and the model, which is just this one up here. And I actually made a little bit of a type of here. It's not parent page type. It is parent page types because it's a list that can be more than one. So if you're screaming at your screen saying, Hey, Caleb, you made a typo Good catch. I sure definitely made a typo there, so I'm just gonna go back to route at a child page, and it automatically says, Hey, I only have one page type that's allowed to live under route. It's a homepage, so it automatically takes me here. All right, so there's another way to go about doing this. We actually solved all of our problems with this one method. The other method is the sub page types. So if we click add child page to our service page, let's go back to our service listing page here. And let's just comment this out and I'm just gonna hit back because I want to see all of these different options. So I'm at my home page. I go into here, I have a service listing page that's the page type here, and I can add a child page, and I can add a service listing page or a service page. Now we solve this problem by adding parent page type is the homepage, But we commented that out. So now we're saying the service listing page can also live under service. Listening Pitch can also live under a service listing page. We don't want that at all. The other method we can do here is we can say sub page types is equal to, and we're saying what page types can we set? And we're saying what page types, what child pages are allowed to live underneath our service listing page, and for this we're just going to say services dot service page and this will get rid of every other option except the one we specify here. This is also a list. If we wanted this to possibly live anywhere else or have a child pages anywhere else, we could set that as well. As long as it does not conflict with the parent page types that are set now that's not applicable in our current situation. So I'm just going to save this. And when I refresh this page, service listing Page is not going to be there. And it will automatically assume that I'm trying to create a service page just like that and let's go check out our tree. We've got a service page here and we can add another service page. Oh, would you look at that? Actually, we can add a service listing page to our service page. We can go Route Home page service listing service, detail, service, listing, service, detail, service listing, service detail. We don't want that either. So let's first of all, we cannon comment that which will solve that problem. But in her service page model, we can also say some page types is equal to, and let's say we don't want any sub page types at all. We're just going to provide an empty list here. That's it. Go ahead and save that. Go back a page. And now we can't even create a child page underneath our service pages. That's perfect. That's where I retreat is going to end. So we've got a home page. That's this one here. We've got a listing page. That's one we're currently listening. All the child pages on Earth, Children pages and then we have the child pages themselves. That's the service detail page of Service Page, and it cannot go any further. So we've effectively said that's as far as this can go down this route. So now we've set up a proper structure for where our pages can live. What pages can be their parents? What pages can be their Children? But we have another problem. Let's say our site on Lee should ever have one service listing page and we say and child page to our home page. We can have as many flex pages is we want. That's what we want. We want as many as the customer of the client or the content entry people want to create, but we don't want to service listing pages. That's confusing for content entry. So let's try and get rid of this one. Now. We do this a slightly different way because we've already specified the parent page types. We've already specified the page types that the child pages can be. We're simply going to say Max Count is one. There can only ever be one service listing page ever in this website. So now I'm gonna go back at a child page, and this automatically assumes that I'm going to be creating a flexor miscellaneous page because there are no other options I can no longer create to service listing pages. There's one more thing we have to do here in our route. We can create another home page if we wanted to. I think we should limit that because this is not a multi site installation of Wag Tail. This is a single site insulation of wagged, also with Wag tail. In Django, you have multiple websites running off of one installation of Django or wait till, but for this case, I just want to only ever be able to create one home page underneath route. So let's go ahead and open up home models dot pie, and we're going to say Max Count is eagle toe one, and that will limit it to only ever have one home page in the entire website. We can also, at the same time, do some page types is equal to, and we know the home page is only ever going to have two different page types. Now this is already taken care of due to the parent page types that we've set. But we can restrict this even further by saying the sub page types can only ever be a flex page that's already an accepted rule and services dot service listing page. And just as a reminder, that's the name of our app, and this is the name of our model. So that's being saved. And when I go to add another page to root, it doesn't give us any options at all. And that is how we go about limiting our pages. Now you might be thinking, this is useless. I don't want to do that, and you're going to be the best judge for your website. Whether that's good for you, your client, your content, entry people, that's a judgment call that you're going to have to make. But I personally like setting this so that when my clients come onto a website like this and they want to create another page in here, another listing page, Well, they can't. That's going to break some of the logic that we have built into this website already. Or instead of being able to create another service page underneath a service page and just going on forever, they can only ever create service pages underneath the listing page and again that helps maintain our logic as developers in the code. So I think this is Ah, really powerful set of features or a trio of features. If you don't think this is useful, you don't have to implement this. This is all optional. Otherwise, if you like it, feel free to use it. It comes baked in with Wag Tail 32. Navigation Menus : Our website is coming together quite nicely, but there's still no real navigation. We have fake links up here that don't actually go anywhere. Navigation is absolutely vital for a website. We just haven't built it yet. So we're going to start by creating new app called menus with the standard python managed up high start up menus. So open up your terminal and let's run Python managed up high start up menus, and I'm just going to rerun my server there. And when I opened up my editor, I'm going to see to close this stuff down. I will have a new folder in here called Menus. Now, we're not going to need views, so I'm gonna get rid of that. We're not going to need tests, so I'm gonna get rid of that. We're not going to need admin. We're gonna get rid of that. So I just have migrations in it. APS and models, not pie. Now, in our models up high file, we're going to end up creating a menu item on the menu class. Okay, So first, we're going to need to create a new menu, a cholesterol model. We haven't worked with cholesterol models But this essentially allows us to save multiple items at once without other objects being created or needing to be created beforehand so they can sort of all save at the same time. Now the reason for that is because a menu when there's probably only one menu, but it's going to have a menu item, another menu item and another menu item, and we want all of these to basically save at the same time. We also want these to be reorder a ble, so we're going to use a thing called an order arable. So I'm gonna clean that up, and the first thing I'm going to add is a class called Menu, and this is going to be a cluster herbal model, and we have an imported cholesterol model. This comes from the package called Model Clusters. Let's do from model cluster dot models import cluster arable model, and now we can use it. Next. We want our menu to have some sort of titles that we can identify it, and we also wanted to have some sort of slug so that we can easily identify it in our template so that when we tried to access this menu from, let's Say, a template tag, we could just pass in a particular slug. Basically, it's a text based I D, and we'll be able to find it pretty easily. So let's go ahead and do. Title is equal to models dot char field Max Learning thing. Let's just give us a max length of 100 characters, and the slug is going to be something called an auto Slugs. Fields. Let's do an auto slug field, and we haven't used this yet, but we will get into this in just a second. So let's say this auto slug Field needs to populate from the title. And what this is saying is, if there isn't a slug, grab the title slug If I it and use that, we're also going to say that this is edible now. Auto slug Field comes from another package, different one, and you don't have to use auto slug field if you don't want to. You can simply use models dodged char field if you like. I like using the auto slug field because it allows me toe be a little bit lazier when doing content entry, and I could just specify the title hit Enter, save the Model or Save the menu, and it will create one for me if I wanted to. Eso let's go ahead and use the auto slug field. This is going to come from Django extensions, which I don't believe we actually have installed right now. Django extensions dot tv dot fields Import auto, slug field and let's see what a terminal saying. It's saying absolutely nothing, and that is because this is a brand new application. We need to add it to our new APS. So in our based on pie, we're going to add menus. That's just the folder name. That's basically what I just call it, the whole lapses called menus. So it's going to match that close that let's refresh our terminal. There we go. This looks healthier. No model named Jango extensions. So we're gonna wanna cancel this Do Pip install Django extensions, Cool beans. Let's rerun our server. It's not complaining anymore, and let's continue on here. So we're not going to run our migrations yet. We're going to add a little extra stuff here, so because we're going to end up registering this with wag tails, model it, man, we need to give this panels if we give it panels. That means that the fields that were telling it to we'll be exposed so we can say, Hey, give it a panel for a title and give it a panel for the slug and those will exist. They will become edit Herbal. You'll be able to actually change the title or add the title and change or add a slug. So it's at a field panel here. This one's going to be title, and let's add one more field panel, and this one's going to be called Slug field panels also not imported. So let's do that. So we're going to do from my tail dot admin dot edit handlers import field panel checks terminal I don't have any type of is presumably, and we're almost done with this part. So next, when we eventually create a migration for this is going to create it a class called Menu in the database effectively. And whenever we use this, it doesn't have a name to call itself, so it's probably going to give it the name of the memory that's allocated. So, like sometimes in python code, you'll see something that's like, Ah, you know, something like that sort of looks something along those lines. If you've seen something like that, that's a little bit of memory that's allocated in your computer. So instead of saying that, that's the name of it, we're going to say we are going to overwrite this with a strings, a return self title. And now we're saying Every time we access a menu, object is going to be called Whatever the title is now, this point, let's cancel our server and let's do python manage dot pie. Make migrations cool. I have some migrations from the last video that we did with some flex page stuff, and I have created a new model for the menu that's your python managed up high migrate. Everything was applied all right, the three runner server, and we're not going to see anything in our advent yet. We haven't registered anything in here, so we're not going to see anything going one step beyond that. We also only have a menu. We don't have menu items, so let's go ahead and adds a menu items Now. Now this is where it's going to get a little trickier to understand at this point, this is You can think of this just as a regular model and as a title has a slug, and eventually we're going to create some menu items and we're going to relate it back to this menu. So I'm gonna create a class called menu item and this is going to be an order. A bowl. Now we don't have order Bols imported yet. So let's do from wayto decor that models import or durable. Now, what kind of features do we want our menu toe Have we want this toe have some sort of link title? We needed to have some sort of link you where l and let's say this is going to be an external link. Ah, we're going to create a link page. This is going to be an internal link and maybe an open in a new tab option, and that's going to be a Boolean. So essentially what we're going to do is for every link item in here, it's going to have some text. It's going to have an internal and external page and the option to be opened in a new tab or not. So that's a little bit of pseudo coding. Let's go ahead and actually add these in here. So let's add models. Char field Blank is equal to true. No is equal to true. Actually, we don't need to add no is equal to true, because Blank is equal to true. Plus, it's a char field, but let's Adam max length of 50. The link you well is going to be an external ing, so this is going to be typically, we would say, a ul field. But this could also be an egg anchor link down the page somewhere. So it could just be a link that looks a lot like this bottom or contact and you'll scroll you down the page. So maybe that's what we want. So we're going to leave this open with HR Field. Give that on max length of 500 because external links to be pretty long, and it can also be blank with our internal page. Let's go ahead and create a foreign key to any wag tail page on our site, so we're gonna say models out foreign key. Wait till core dot page they were saying. Anyway, tell Paige and every single one of our pages is inheriting that regular page. So if we say we check out home, our home page is actually importing this page. And if we come up here, we're going to see that is coming from Wait till core models Page right Don't core dot models page make decor Being the AP page being the model no is equal to true. Yes, there's gonna be no blank is equal to true. Yes, this could be blank. That's optional. We're going to give us a related name as well, but we're not going to give it any special related name. We're not going to use that. So we're basically just going to discard that Related, named by using a plus sign and then on delete what happens when we delete whatever pages is being linked. Teoh. So if we linked to a contact page and we delete the contact page, what happens to this menu item? We're going to say this menu item gets destroyed by cascading and then lastly, we have open in new tab and this is simply a Boolean field, so a boolean field is a true or false default is equal to false and blank is equal to true so it could be optional does not need to be checked. And because again, we're going to be displaying this in our way. Telemann. Eventually, we will register it in the Wag telling men the model it man, we need to say, Hey, wagged Oh, we want you to actually show all of these. So let's go ahead and create a field panel for the Link title. We're going to create another field panel that's getting a little low there. We're going to create another field panel for the link. You are l that external link you are. L probably name that external link you Well, if we wanted Teoh, we're going to create a page chooser panel for the Link page. That's our internal page. If it's selected and we're going to create one more field panel for our Boolean field called Open in a new tab, we don't have our page chooser panel imported yet, so let's go ahead and import our page chooser panel. Now that's all well and good. But menu item and menu, other than being in the same file, have no relation to each other whatsoever. We need to create some sort of relationship. Now what I'm going to dio is I'm going to run migrations again and migrate them run server . And that's just going to actually create this menu item for me. Now I want to link this menu item to the menu itself, and we're going to see that could be done with a thing called a parental key. And it's going to link to the menu class, and it's going to have a related name we're actually going to use. This related name is going to be called menu items. Now let's go ahead and import parental key, and I can never remember where this one comes from. Eso You might actually see me make mistake here. I think for a model cluster dot fields Import perent. Uh ha. That's probably the first time I ever got that right on the first try. Parental key. So now that saying Hey this page or doesn't need to be called Page, it could literally called anything. It just needs to have some sort of connection to our menu here, so let's save that. Let's run migrations once more, and it's not model cluster field. I knew I was gonna get that wrong? I think it's model closer fields. Question Mark. There it is. Okay, let's go ahead. Make migrations. And now this is asking Hey, you're trying to add a non gullible field called page to menu item without a default, we can do that. The database needs to populate with something in there. Let's go ahead and select option number one, and I am simply going to type none. Go ahead and migrate that rerun her server. Okay, Things are looking a little better when they weren't looking bad anyways, but things were looking like they're starting to sort of shape up now. That's Ah, let's go ahead and actually register this with Wag Tail so we can actually access it in here. Now there's two ways we can do this. We can register this as a snippet or we could just register this in the menu of section here. I like registering it with the menu section. It just feels a little better. So beside models, not pie, I'm going to create a brand you file here called Wag Tail hooks dot pie and it absolutely needs to be called. Wait till underscore hooks dot pie. So in here. We're going to do two different things. We're going to import to the model Edmund, and we're also going to import the Decorator Deco rate. Or and then we're also going to create a new class. We're going to call it men, you admit, and it's going to be a model Inman, and we're going to register this. So let's go ahead with the 1st 1 and just do the import from Wait till dot con Trib motto Edmon dot options Import model Littmann. We're also going to import model admin register. We're going to then create that class inherit model Litman the White Tail model Lydman We're going to say, what what? Which model does this come from? What are we actually admitting here? We're going to say, register this menu item now We need to import that as well. So from dot models, important menu dot models comes from the exact same folder so we can just use dot models that just goes right over here. Import the menu class, the menu label that's got menus. The menu icon is going to be a list You l also looks sort of like a menu does need a comma there. The menu order. This is where it's going to be ordered in the menu here. So I'm gonna say 200 actually don't recall exactly where to hundreds going to place us. We'll find out at settings. Menu is equal to false. And that's saying we don't want it to show up in here and exclude from Explorer is equal to false. Okay, I'm just going to restart Django here just for good measure sometimes. SNC If it had restarted on its own, we wouldn't have seen that trailing calm has not allowed without surrounding parentheses. And that's because I hit. Enter should have just been on one line. It was on to on the last thing We need to register this model admin register, and we're just saying, Take this whole class model Inman and registered with Wag Tails Menu system. So let's go ahead. Let's check it. Wait till let's give this a refresh and wait for it to show up over here. Come on, There it is. Number two menus. Let's go ahead and add a menu. We're gonna call this menu header. You can see that it auto fills as I type. That's thanks to the auto slug field. I'm gonna call this one header and you'll notice that we have a menu in here. But we don't actually have menu links. We need to add that in. So let's go back to our model Stop high. And even though we have a parental key to our menu here with a related name of menu items, it's not actually being used in the database that it is being used. But we just don't have anything actually, in weg jail terms being exposed now because there could be multiple items, multiple menu items and because we are going to be using an order herbal, which allows us to move a menu item up or down. We want to use a thing called an in line panel, and this is going to take the related name. That's the menu items, and that just relates to this related name here. So it's the exact same text, and the label is going to be a single menu. Item in line panel is not imported, so let's import that terminal and is not complaining. This is good news. Let's go ahead, refresh this page and now we can add a menu item and another menu item and another menu item, and we can mix and match these in whichever order we want. Let's add a new menu item, and this is going to be a link to our services. And we're going to say that this is going to be a wag tail page when we're going to create another one in here about us and we're going to link to another way Detail page called about us save. And just like that, we have a menu system in place on the back end, but on the front end want mop? It still doesn't work because we have not implemented anything yet. And the next lesson we're going to create a template tag where we can actually go ahead and grab this menu from our template and loop through everything we need to loop through in order to create our menu up here, we're also going to create the foot or menu. Down here is going to be the exact same menu, so we'll use it in two different places. 33. Navigation Menu Templates : Okay, Dokey. In our last video, we created a menu system where we could actually go ahead and add a menu. We have multiple menus if we wanted to, but we created one called Header. And if we edit this, there are two menu items in here which we could move up or down, thanks to a thing called in Order Arable. However, we still don't have access to that in here. Now there's several different ways we can get access to this menu on every single page. One of those ways is through a template tag. We're going to take that route instead of creating a global object that's accessible on every single template. We're just going to use the template tag way. So we're going to create our first template tag. Now I'm going to go ahead close that we're going to create a new folder called Template Tags all one word, and then here we're going to create another new file or the first new file in there called Menu Tags dot pie and that name is going to be important, and I'll reference that in a little bit. Now, here we're going to create a simple Django template tag. So when we open our header dot html, we've seen other template tags, so we've got static as a template. I we can see it being used here. That's a template tag. When we use wait till images, we use the image template tag. We're going to now create our own template tag, and this one is just going to be a simple tag. Let's go ahead and say, from Django, Import template from Let's Go to folders up or yeah, one folder out, Let's go one folder up, Go to models and import our menu. We're going to need to add Register is equal to template dot library, and that's gonna help us actually register this. This tags that we can use us in a decorator fashion. We're going to say, deaf, get menu. We're gonna pass it a slug in the template and then is going to return a thing. And for this to actually become a proper template tag. We need to then say register dot simple tag, and now we can use us well. And now we can use this inside of our template, and it's really as easy as going into Let's say a header file and saying Load menu tags. Now remember when I said I'm going to reference that name again? Menu tags is a menu tags up high file that's important. These names are going to match. And what that says is all the tags that are available in this file are now allowed to be used inside of our template. So we now have access to a template tag called Get Menu. So if we go ahead and actually call us US, I get menu. It takes one parameter a slug. Let's call that header and where I'm getting that from is the slug header. That's why we have a slug in here so we can select it. Alternatively, you could use the ideas well, but I ds they change over time. If someone accidentally were to delete this header and create a new header that I d is going to change and you'll need a deaf get involved in, that's just no way to live your life. So if you use ah slug, that problem is actually fairly easily remedied. And now we're going to said this as a variable called Navigation. And in here I'm going to add H one and I'm going to print out navigation to go ahead and refresh our page here. Oh, menus. Tags is not registered yet. Okay, that is super coming. So one of two things happen here. Either I did not save this page or because sometimes when you work with template tags, even though you've made a change, Django doesn't always notice that template tag files change. And so it's not going to restart itself. So I'm just going to give Django a little bit of a kick here and restart my Django Server three freshness. There it is. Ah, thing. Now in the template, it doesn't say a thing in here. It says navigation. Now to track this backwards, we have a very book called Navigation and we have a function called Get Menu with a parameter of Header. And then we assign it as a very bill called Navigation. In our menus tags, we have a function called get menu. It's only parameter is a slug, and it simply returns this. Now we need to actually go and look for this menu item based on its slug. So let's go ahead and give this a try we're going to return menu died. Objects Doug get slug is equal to whatever the slug is. And if you're wondering, hey, were you getting slug is equal to slug? We're saying menu dot slug has a field called slug is equal to the parameter called slug. We could easily swap this out to say something like the slug if we wanted to make it a little less confusing. I'll leave that up to you. If you want to do that, let's run an exception here just in case those menu object does not exist. Let's do an exception. Menu does not exist. Return menu dot objects dot none, and what this is going to do is if we can't find a menu that has the header, where has the slug of Header? It's going to catch that exception because Jenga will throw us an exception and is going to give us a quick reset with nothing back in it. So it's still giving us an honorable so that when we go to eventually Luke overall over menu items in our template, it's not going to give us any sort of problems. So I say that I'm still going to give this a little bit of a kick even though it looks like it did it itself that time. And when I refresh here, it simply says Header. Now the reason that this says header with a capital age is because the title is called headers. Let's say header with, Ah, different name and let's go ahead and just change that too. Header save and I'm gonna refresh my template again. Header with a different name. If you're wondering what that's all about in our menu itself, remember, we set Dunder Stringed under and it's just returning self well, that title. That's what we set here. Also, that's what we set here. So the title field is header with a different name. Title field is header with a different name and the string the string representation. The string name of this particular object is called, whatever the title is, so that's where we're getting that from. So if you see that chances are you're just getting the menu object itself and you need to loop through it. So let's go ahead and loop through this now and I'm going to get rid of that. And where are our here? It is four i in 123 So this is just going to create three links for us, and we just want to say, instead of four I for item in navigation dot menu items dot all now this is again going to get a little bit confusing. So we're saying for the navigation that's the whole menu class, the whole menu object this entire page essentially dot menu items in our code. We've got an in line panel with a related name using menu items, so it's grabbing all of these menu items dot all so it's going to get all of them and we're going toe loop through each one, all right? And let's just do a quick little test here and let's print out the menu link text. So let's go back here. And what is that going to be? The Link title is what it's called menu dot link title or item darling title. Rather. So for every menu item we have, we're gonna call it item, and then we're going to get the link title. Let's go ahead refresher page, and here we can see services and about us, so it's actually working. Now we need to swap out this link here, and we're going to do this in an if statement and then we're going to collapse this if statement to make a little bit more sense. So we have one of two things we've sort of worked with this before. We have one of two properties we can use. Link. You are L. Which is going to be just regular text or a link page. So let's always check to see if there's going to be a link page first. So we can say if item dot link page, we can print out item dot link page that you are l and we do the dot you, Earl, because that is going to be a way detail page. We can also do. LF item dot What did we call that link? You are l This one is just regular text. So weaken dio item don't link you well And let's put else in here an empty link basically, and this is what it's going to look like. So if we read this, that's going to say if there's a wag HelpAge a link page print Oh, that you Earl, if there is not one, but there is a link. You well, an external link. Print out that text, whatever that ends up being. And if there's neither of those, just show us a regular number sign as a link, and that link is not going to go anywhere. So let's collapse all this together. So all I'm doing is putting this on one line, one rial gross line. Gonna cut that, and I'm gonna move it right into here. Now, let's say that and refresh our page. And when I click this services about us Oh, look at that. We've got a navigation system working, Okay? But we have one more thing that we we've added in here that we haven't implemented the open in new tab. So in our link here, we also want to say okay, if the item is supposed to be open in a new tab, let's give this a target of Blank. And if no, I haven't set any of these links to open in a new tab so you could test that out on your own. But I'm just going to leave this as is, cause I'm pretty happy with this now. We've also worked with If statements like this before, and we've actually moved us into Mawr Python logic so that we don't have too much template logic going on. And this page has a fair amount of template logic. So what we can do here is we can simply say item dot link, but we have to create this first. So let's go back to our models, that pie. And in our menu item, let's add a jangle property in here with a decorator at property Deaf Link self. And this is going to be the dot link in the template that we can You. So now we can use item don't link whatever this returns, That's what item don't link is going to be. And we can say if self doubt link page then we can return self dot link page, Don't you, Earl? And this is really the exact same logic that we just put in our template. We're just doing it in python house are templates can stay nice and clean. Now we're gonna say LF self dot link you are l and again I'm just getting those fields from appear I think page link you well, return self dot link URL otherwise always return a simple, empty link, and that's just going to always return the string. The other thing we said was the link title can be blank, so technically the link title might not exist. Let's go ahead and say, You know, if if it is blank, let's assign it's something different. So again down here, we're going to create a new property and we can use this inner template as well. Property deaf Title Self. Now what we're going to say is if there is a link page and there is not a self dot link title, so basically the link title has been ignored, but there is a Wag Tail page has been selected. We're going to return that Wait till page titles itself dot link page dot Title is what we're going to return. LF there is a self dot link title return the self dot link title and lastly, if there is no link title and we're using an external link to some other page, we can just say missing title Now, in our template, we can use dot link and dot titles. Let's go ahead and swap these out for the title and this is going to be nice and clean with item dot link and that is it. Go ahead and refresh this page. And sure enough, it looks like nothing changed, but we managed to simplify our template. One last thing is we want to now add this to our footer. So this is very, very easy. It is literally the exact same thing as a header. We're going to copy that this whole line here, so do to do its copy that we're going to do the same thing as we did here. We're going to do the exact same thing here, and basically the exact same menu that's going to be used in the header is going to be used in her footer as well. So we're going to use it in two different places. So it's open up our footer dot html. We need to load in menus tags that matches our menus, takes up, I file paste that guy in there. So we're going to run the get Manu function. The one and only parameter for the slug is Header. We're assigning this as navigation and instead of four I and 123 we can say four item in navigation dot What did I call that again? Menu items? Not all. And in here. Let's just do a simple test item dot title and menus tags not registered. Oh, interesting, Interesting. Okay, it's because I called it menu tags and that scroll down to the bottom. And there we go. We got to links services and about us. And if we ever wanted to change the order of that, we could literally just press that down button or the up button on this one hit save. And when I refresh the page, it'll be about us and then services and then about us and services, about US services and confirmation about us and services. So that's where the order bill comes in. The one last thing we need to do here is where is that for Loop? We need to add that link item dot link, and I thought that was going to last one, but I forgot that we have If item dot open in New Tab, we want this to target Blank, and that's refresher pages. Make sure things are working as expected. Let's go to the services page. Yep, go to the about page Yep. And we have a working navigation system. Now there's a non arable package mentioned here called Wag Tail Menus. If all of this is just too much and you don't want to deal with all of that head on over to our K H, I can't say that one but his cult way. Del menus it's on. Get hobbits on pie pie and you can read through the docks here. We're not going to cover this in this course, but it creates a really nice menu system for using Wag Tail, and you can get into, like, some menus and things like that, which we currently aren't using. So if you need a more advanced menu, this might be the way to go. This is a really good package, it seems to keep things nice and simple, and you don't have to manage your own menu system, which is really, really nice. So again, if these last two videos were a little too much for you, feel free to use the Wag Tail Menus package 34. Contact Forms Part 1: pretty much every website these days needs a contact form. Not all of them, but most of them do. It's an easy way for your viewers, your readers, your buyers to contact the owner or article writers or website maintainers. Anything like that. Wag Tail gives us the nice ability to create special contact form pages. So first thing we're going to do is let's open up our terminal. Let's cancel that and let's run Python managed up. I start AP. This one's going to be called contact, and it helps if you take start up correctly. Let's rerun her server. Hide that and in our code, let's open up based on pie and we want to had contact in here. No contact is the name of the app that we just created. So if you called yours contact page, you would change this to Contact Page, and your folder would also be called contact Page. Your APS would also say Contact page in here. So let's close that down and let's get rid of some files because this is going to be a wag tail page. We don't need our views. Lots of tests were already written, and we don't need any sort of admin stuff. We are going to keep our models APS in it and migrations, though. Now, to create a contact page, we need to do a few things. We need to first create a class contact page, and you'd think that this would come from Page. But it's not going to come from Page is going to come from this thing called an abstract email form. And the import for that is, if I can remember this correctly, it is rectal dot con Trib dot forms dot models import abstract email form he had that looks right to me. No, don't forget at any point time If you don't remember your imports, that's okay. You can always check out the docks, these air all available on the docks as well. This code and at any point in time, if you ever like oh, I want another working example of pretty much anything that's in this course. You can check out the source code for this course, or you can also go to http. Get herb dot com slash coding for everybody slash learned. Wait till that's the original learned wag. Tell course all the source code for the original learned Wag Tail courses in there. I think there's something like 50 videos, so there's a bunch of commits probably like 60 ish commits, and everything split up into different commits. So if you just go over there, you can check out all the source code, and you can split it out based on commit based on video as well. So feel free to check that out if you're interested in seeing another example of how a lot of this stuff is used. Okay, so in our contact page here, we're going to specify template because I like doing that, even though by default this would already be contacts last contact page. But in our contact page, we also get another one. We get a landing page landing page template, and we can specify this one. Now if we don't specify this one, I believe what Wag tail will do automatically is just a pan append the word underscore landing to your contact page. Now we're going to you specify that anyway, so contact slash contact page, landing dot html, and I make that one step smaller there. Our contact page should have any sub page types. Some page types is equal to none. We don't want any child pages under here and in a little bit what we're going to do is in our home models that pie. We're going to specify contact dot contact page as a sub page type. It's allowed to live under the homepage. Now. I'm not going to save this file yet because according to our code, none of this exists yet. I have not saved this file yet. How many contact forms can we have? I'm going to say there can only ever be one like a highlighter. There can only ever be one. And what kind of fields do we want in this contact page? I want an intro that's going to be some sort of rich text field, which I don't believe we've actually worked with yet. So this is going to be, ah, good way of showing you how to use way tells rich text with the wig editor called draft tail inside of an actual page. We're going to, ah, create some thank you text think you text. This is also going to be a rich text field just because of the design that we're working with. I know that there's going to be a map image, and this is going to be a foreign key to a wag tail image and possibly a map. You're l If someone wants to contact us and this is going to be you are all field. So I'm just doing some pseudo coding here, not actually writing any of these fields out? Not yet. Anyways. We have these fields and we want Wag Tail to expose these fields for us. We're going to say content panels is equal to, and what we've always done was content panels is equal to page dot content panels, and then we append a list to it. But we don't have page in here yet. We have abstract email form, and then we can add our field panels and stuff in here. So let's add a field panel for our intro field, which currently does not exist. Let's add an image chooser panel for our map image. That's at a regular field panel for our map. You Earl map, you are l. Now here's where the special stuff comes in. We need an in line panel to our form fields we haven't created this one yet, So this one's going to be a little bit special. We're gonna call this one. Give us one A special label called Form Fields. We'll come back to that in a sec. We're really just mapping out our content panels right now, based on fields that don't even exist. So we have some pseudo code, we're gonna fill this out, we'll fill these out, then we're going to add our form field. So we're sort of all over the place because we have several different things that we need to work on in order to get this up and running. I also want to put a field panel in here for the thank you text. Thank you text. That's going to be a rich text field panel. And we also have a field panel for a from address. And we have another one for the to address, which we did not set. But that does in fact, come with our abstract email form. So if I jumped a definition here, this is the source code. We have a to address a from address and a subject which we have not added yet, so let's go ahead and add a field panel for the subject. Now, at this point time, if you were to save this file, your terminals going to freak out, it's going to say a lot of things are not imported and we're going to get there. We're going to do all the imports roughly at once. Let's go ahead and fix up some of our fields now. So this intro is actually going to be a thing called a rich text field. A proper rich text field. Is this allowed to be blank? Yes. And what features do we want to enable? Now, this is a lot like the rich text block that we added for our stream fields. Only this one is on a page field. We're going to enable bold I not italic link ordered list and a on ordered list. And our thank you text is going to be very similar as well. So we're going to say blank is equal to true and the features are going to be board link ordered list and a Nord erred list. Our map image is going to be a foreign key, so we do models dot foreign key and this is how we end up selecting a wag tail image. Wait. Tell image is actually plural images dot image. We're gonna say this can totally be no if we wanted to. But when you're creating this page when you're editing this page, this cannot be no. So we're going to say Blank is false on delete what happens when you delete this image? We're going to say models dot set No. So it's not going to delete this page. It's just going to set this field to know help text. Let's say the image will be corrupt Teoh ass something like, I don't know, 5 80 pixels by 355 pixels dish. And the related name is going to be plus cause we don't need that related names. So it's in a sense, throw that away. The map. You are always going to be models. Start you are l Field Blank is equal to true. And let's add some help text and let's say this is optional. If you provide a link here, the map image will become a link. That's the idea anyways, and so now we have all sorts of things that we need to import. Ah, but even before we do that, we have this form fields. We have some form fields in here that we need to actually set. So let's come back up here and let's create a new class for our form fields. Now, we've sort of worked with some of this already, so this is going to look somewhat familiar, but it might also feel a little magical as well. So this form field is going to inherit the abstract form field. And in here, this is going to look pretty bizarre, but it's going to be Page is equal to parental key. This is going to go to the contact page that matches the class. Name on delete is equal to models dot Cascade. So we're gonna delete each of these form fields when the contact pages deleted and related name. This is the important one form fields. And the reason it's important is because that related name matches the in line panel. All right, looking at a terminal, we are going to have to deal with a lot of different imports here. So I'm gonna try to rattle some of these off off the top of my head based on, you know, sort of what we see on the page. So we need an abstract form field here. I believe that comes from the same place that the abstract email form does. Let's import a bunch of our panels. Now, let's do from wag tail dot Admin Dodd that it handlers imports. We have a field panel being used. Ah, we haven't in line panel being used in line panel and I think we only Penhall not pain. Uh, and I think the only other one we have yep, is an image chooser panel. But that comes from somewhere else. We're going to say wayto images dot at it. Handlers import image Chooser panel. We need parental key. That one comes from model cluster from model cluster dot models import parental key. What else do we have? We've got rich text field. It's a new one for us from Wait till dot core dot fields. This one is really these air really starting to stretch my memory here. I think it's fields rich Text field? Yeah, I think its fields and I think that might be It looks like it. Okay, so let's go ahead. Cancel or server. Now that that's working Let's do python manage dot pie Make migrations until we've got a new model for a form field and a new model for our contact page. Let's do Python managed up I migrate and we'll rerun her server on Port 8000. Now let's go to our page is here. Let's try to create a contact page. We have a home page here at a child page, and it still only allows us to do the flex page. That's because on her home page it has allowed one of two page types. We covered this Ah, couple lessons ago, but the service listing page and the Flex page are the Onley sub page or child pages that are allowed to live underneath the home page. So it's trying to create a flex page because the service listing page has a max count of one and it already exists. So the only other option is a flex page. We can album say, Hey, add that contact page, so I'm gonna go back and then at a child page, and here we go. We have a new option, a new contact page, and we can see that this looks a lot like a regular page. So we're gonna call this contact us. I want to change that slug to just be slash contact because I like that an intro is going to be Hello. Welcome to my intro text or something. Boring my map image. I don't have one, but I'm going to use a regular image here. A map? You where? L is just going to go to http maps dot google dot com And now we have formed field, so this is really cool. Now we can create our own custom form. Now, a thing to keep in mind is this does not replace Django Fields. This is a contact form. It is not meant to be used to add all sorts of additional logic. So if someone fills out this form, it should not automatically create a new wag tail page. That is more or less for the Django form system. Not for the wag tail forms. At least not for the contact page anyways. Ah, we've got a label help text. Is the field required? We have different field types in here. A bunch of them. We'll learn how to limit those in the next lesson. I believe choices and a default value. So let's just go ahead and add a few of these in here. So let's add your full name. And this is going to be a single line of text and it's required. Let's add another one in year. This one's going to be your email address. This is going to be an email field. It's also required. And let's add another one in here called message. And this one is going to be a multi line text. Thank you, Texas going to be. Thank you. Thank you. Thank you. Thank you. Thank you. Thank you. The from email address. Who is sending this email now? One thing to keep in mind is when you launch a wag tail site, your white calcite is not going to send emails by default. You're going to need to set up SMTP or use a service like postmark, which I think the website is postmark app dot com or something like send grid or ah, aws has a service called SCS. Something like that. So 1/3 party service to send emails, that's usually the easiest way. So the from address here is going to be Caleb at learn way, tell dot com. The to address is going to also be Caleb at Learn wag tail dot com. That's we're going to send the email to. So basically is gonna look like I'm emailing myself. But sometimes that's how contact forms work. Or I could email someone else and it looks like it's coming from my email address regardless. And the subject is going to be contact form submission because I'm not feeling creative with that name. Ah, you'll also notice before we save. We can preview this page or preview the landing page. We'll get to that in just a little bit as well. Cool beans. That page was saved. And if we view live, we're going to see Yeah, of course, Template does not exist. We knew that this was coming. We didn't create a contact page. We need to create that template now, so I'm going to go into rocketman templates and create a new section in here called Contact and Contact page dot h e mails. What it's going to be called this is going to extend based on HTML, just like all of our other pages that's going to load. What kind of stuff are we going to need? Well, we don't know yet. So I'm going to actually comment that out, actually, just delete it. Let's bring that back. Ah, Block. Let's create a new block for the content. And just to make sure this is working soon H one hi from contact Page. I don't really mind that there is a little type of when their cause we're throwing it away high from contact page. Now we know we have some rich text in here because if we go to our well, let's close the home page one. But if we go to our contact page nope, that source code, we go to our contact page. We have Rich text field, so we know we're going to need to display that. So let's load Waittil core tags and the field is going to be intro. So we are simply going to display that will do page that intro and throw the rich text filter on it. Hello. Welcome to intro text. That is what I put in the page. Now what I'm going to do is because there's a bunch of html on this page and I don't think you're interested in just watching me type html on Emmett form her arm like tive, Whatever. I'm going to write on my code. I'm in a black of the screen and come back when I'm done and we'll fill in all the missing pieces together. All right, so I just wrote out a bunch of HTML and we're going to fill in the missing pieces together . So what this template is now going to look like is there's a title, some intro text we need an image may be a link to a map, you ero, and we need our form to actually show up and render properly and submit. So the first part pretty easy. We've done this before page dot title and our rich text. We literally just did this just a couple of moments ago. We could do page dot intro pipe rich text, and that's the rich text filter that comes from Wait till court tags. Next. We have an image in here, and this image is going to be 5 80 by 3 35 Let's go ahead and use the image tag. And if this image tag does not work for you, that's because you need to load wag tail images tags. So we're going to load the page. What did we call that image? We called it Map image. We're going to say, Feel that with 580 by 3 35 as I m G. Let's go ahead and replace this value in here. I am g dot u r l. We've done this number of times already. Always add the salt and let's take a look at what her page currently looks like. All right, look at that. That's starting to come together pretty nicely now. We need to add our form. There are a lot of different ways of doing this. The way I like to do this is sort of loop through all of the form. Fields one by one sort of identify what they are and style my page differently based on what the field is. So, for example, if if I'm looping through all my form fields and I have a text area, I might want to add something different versus a regular input field. But as a shortcut for now, we can do form dot as you l What I have here also is a form method is equal to post the action is the page where l It's just this page that is going to submit onto itself. Always, always, always. Add your CS R F token. This is your cross site request. Forgery token. This basically means that you are who you are, and someone's not trying to pretend to be someone else. That's not going to stop butts from submitting this contact form, but it will prevent some malicious people. And then we're going to say form dot as you. Well, just a sort of print out the entire form in the form of a list, and I believe it's not actually going to create a list for us. So we need to add UL around it. Okay, let's go ahead and refresh. Look at that. Your full name, your email and your message. That is what we have written. That's what we've added on her page. So when we edit our contact page, go down to her form, fields your full name, we've got email in here and we have a message which is a text field. And sure enough, all of those show of as expected in the next video we're going to continue off with this. We're going to make this look a little bit nicer, and when you submit your page, there's going to be a landing page currently doesn't exist, so we're going to add that as well. 35. Contact Forms Part 2: leaving off from the last lesson. We created a contact page, and we are working on making this form look a little nicer. And then when we editor page, we also want that landing page shop, which currently doesn't. We gave it a template that doesn't exist, so we're going to need to make sure that that works as well. So I like to sort of loop through all of these these fields and is a very laborious task. So you can actually just spice this up using just CSS, which is I think that's the recommended way of doing it. But I also like to sort of spices up a little bit more using a tool called Widget Tweaks. So I'm gonna go to my server here, hit, cancel and do Pip install Django Widget. Spell it right, which it tweaks. And that insult version 1.4 point five. Let's go ahead. Run my server again. You can run your server, definitely run your server and let's go ahead and add this to our requirements. So we have Django extensions in here because we install Django extensions in ah earlier class. If you did not add that in there from the earlier class. Definitely make sure you have that, because you are using it on this project and we're going to add to Django Widget Tweaks version 1.4 point five. And if you are ever uncertain about which version you're using, you can always do Pip Show and then type the package name. So Django Widget tweaks and this will tell me that's the person that made of this email address. That's person's name and the version is 1.4 point five. Save that file, close it, and let's go ahead and install widget weeks in our app. I'm going to throw this at the bottom because it is 1/3 party. So widget tweaks and let's check our terminal. Make sure nothing complained. There are no complaints. That's looking good. And now what I'm going to Dio is I've actually already pre written this particular lesson just cause there's a lot to it. And if we stay here together talking about being able to loop through every single form field and get all the different values we're going, we're going to be stuck on this lesson for several hours, and I don't want you to be bored to death and having to be here stuck with me for several hours. So what I'm going to Dio is paste in my code and we will go through it line by line. Okay, So I just pasted a bunch of code in here Before we do anything, we need to make sure that widget tweaks is allowed, which it tweaks is going to be the file that we're going to load into this template. And let's go ahead and give this page a quick little check. See what happened here, Okay, Missing a refresh my page. And all that was was I was missing an end if in there s so I didn't make you watch that because that took me about five minutes. Actually find it in this mess of code s. So this is a good candidate to split your code out into different include files. But what I did here was I added widget weeks. So we've pip installed widget weeks, Jenga widget weeks and ah, we need to now add our form in here. So our form is going to say for every field in the form, create a new row and they were going to say if that field widget type is a text area, show some text area stuff. So in the text area we have a form group and we have a label in your So this is doing the field dot i D for label and then the actual label itself. And we're going to say if it's required, throw in parentheses the word required. Then we're going to use widget weeks, render field template tag going to render the particular field itself. Whatever classes it decides that it needs, it's going to add it in there. We're also going to add form control and rows of six, and the place holder is going to be whatever the field label is now, This again is a very, very manual way of doing this. But this allows me to add things like rows of six or or class form control so it matches with boots drab. You can do all of this with CSS as well, so you don't actually have to get into the nitty gritty. If you can just do it the CSS way and override any of your styles. That is definitely easier, but this is the more manual way. If you're ever interested in doing this, if there are any field errors for this particular field, we're going to show some small text and we're going to loop through each of the field errors and then show each field air with a line break after it. And then, if there is any help text in here. We're also going to show the field dot help tax. We're gonna make sure that it's safe so that you could put bold in here or a Tallix in there anything like that. Then we're saying, If the field type is pretty much any other field type and the field label is not empty, we're really just going to do almost the exact same thing. But we're going to read to the field with a form control class, which we were doing before, but this one doesn't have six rows. This only has, well one row because it's going to be an input field of some variety and basically doing the exact same thing over and over again. Now, The reason that I'm checking for the field label and to see if existent or not is because if we ever added Google recaptured to this, we're going to need to somehow detect that, and that is one way of detecting it. So again, I'm not going to go through all of this to in depth. But this is the very manual way. So in the future, if you ever like oh, hey, I need Teoh, loop through all the field errors or loop through all the fields and then loop through all those airs. Yeah, feel free to just steal some of this code in here because, hey, I've already done the work. No need for both of us to do it. So when we go back to a page and give us a little bit of a refresh here, we can see that says, You're full name. It's require your email is required and your message is required. Now, if I feel this out, my full name is going to be Caleb and my full email is going to be kill about learned. Wait till dot com. I've got alarm. If some message in here, I'm just going to submit this now. Now, while this submits, there's going to be two things happening. This was the 1st 1 it's going to take us to our contact landing page, but our you where l hasn't changed, it's just using a different template now. And the other thing is, if we look in our terminal, we can actually see that is trying to send an email, His said. My full name is Caleb. The email address that I filled into the form was Caleb. It learned wait till dot com and the message was 50 words of warm ipsum. That's exactly what I submitted. Now that's pretty nice. But also, when we go back to our wag tail ad, men just wait for it to load. We now have a little section in your called forms, and what's really nice about this is every time someone submits your form on the contact US page, you're going to get the submission date and the different fields in here. You can also select all these. You can download them in a C S V, even delete your selected messages, and you can filter through them. So now you have a place to store the email submissions because, hey, you know what? Sometimes sometimes e mails get lost in the Internet. That's just a fact of life. It happens from time to time. And if that ever happens, you have a backup emailing here and in the future. If you ever added another contact form, we won't on this project because we have a max count of one on this page. But if you ever added a second contact for called contact us about our services, this title here would be right under here. There'd be a second row, would say, contact us voter services because I'd be the page title and still a contact page. And whatever your form fields are on, that particular page would be displayed here. Now we have one more thing to do. We need to add this contact page lending. So what I'm going to do is did it Ah, this contact page, I'm really just going to re save this as contact page Landing the title doesn't need to change, but I can get rid of a lot of this stuff in here. And I don't think I need really any of that. And I definitely don't need the form because we're basically just saying thank you. And now this page, instead of saying the intro is going to need to say to thank you. Text to page that. Thank you. Text that rich text. We don't need our images in there. We don't need widget tweets in there, but we do need our core tags for the rich text filter. Okay, let's go ahead. Save that. Give this a refresh. No, I don't want to resend that. So I'm not going to refresh. But what I am going to do is go to that contact page and just preview it. Preview landing page In the real it says, contact us. Thank you. Thank you. Thank you. Thank you. Thank you. Or, you know, whatever. Sort of thank you text you would like to put in there. Probably something a little more appropriate than thank you. Thank you. Thank you. Thank you. Thank you. And that is the very long way. The long winded explanation about how to create a contact page with my tail. Now, to be totally honest with you, I don't write this out every single time. I have my source code available here and also on get hub dot com slash coating for everybody slash learned dash, wag tail. And you can literally just copy that code. I don't think too many people are actually writing all of the contact form stuff out manually. If you are. Hey, kudos. But that's just a little too much work for me. So I usually go to copy and paste route and just change whatever I need to change in here. So once you have that up and running, feel free to either experiment with it. Try to break it if you want Worst case scenario, you just delete this app. Run migrations, recreates the app, run migrations again and start a new or instead of experimenting. If you're totally tired of contact forms like I am, let's go to the next lesson and learn about one more thing about contact forms, and then we're done with it. 36. Limiting Contact Form Choices: all right. There is just one more lesson about contact forms in here, and this is a fairly common subject. At least I get this question quite often and it comes up in a lot of client questions, actually, is how do I limit these now when you're working with a professional design? So maybe a designer has made a design for you? Chances are they have not thought about what Ah date time field a date radio buttons, multiple select drop down check boxes. Jack, you are L number, email. Multi line and single lying texts are all supposed to look like chances are, they said. Here's a single line text, multi line text. Here's Ah, check box on, maybe a drop down. And so we need to actually limit some of these because we're not going to be using all of them. But how do we do that now, in waiting till it's actually not really all that hard to do, but it's not a super easy feature to sort of figure out either. So in my code, here I am in the contact slash model Stop I. I have a form field here called an abstract form field. Now I'm actually changes to custom at abstract form field, and I'm going to create a new class called Custom abstract Form Field. And this one is just going to be an abstract form field itself. So really, we've just moved the class sort of down. We're going to say this one has a field type of HR field and because in Django you don't have a choice field, you have a char field and you can give it to choices. We're simply going to you say, this is a chart field with choices. Let's give us a verbose name for most name is equal to field type. The max length is going to be somewhere around 16 characters, and the choices are going to be form field choices. And for this one, let's just touch this up and say the class and also move that up and make that bigger. The meta class is going to be an abstract class. That's true sort order. Okay, so we're missing one more thing in here. We're just missing the form field choices. I can give you the form field choices, or we can right click on your go to definition and this is really just exploring the wait till source code and we can see in here. We've got a field type. Look at that. The abstract form field. We've got a field type. It's the only one that we are overriding. We have formed field choices here as well. We also have panels and stuff. If we ever wanted to add custom fields, we could totally do that. That's pretty nice. We're not going to get into that. We just want to find out what this form, field choices, what they are. Let's jump to that definition. And we have all these form field choices. So I'm gonna grab all those copy, um, and paste them into my file and so we can see we've got single line, multi line email number. You're out. Check box checked boxes, drop down, multi select radio date daytime and hidden. Well, let's go ahead and get rid of the ones that we're not going to be using on this website. Now. This might be different on your website, but on this particular website that I have made, I don't need any date fields. I don't need radio. I don't need multi select drop down. Maybe, but no. Don t check boxes. Possibly you, Earl. Ah, number field. I don't need a number Field. I really just need single line, multi line email and you are all and this is for translating your text. And if we go back to that regular page, we can see that it's being imported here. So I'm gonna grab that import and imported into my page as well. So from django dot you tells that translation import, you get text lazy as underscore. Okay, let's go ahead and save this. And v Bo's name is not a thing. This is why we check her terminal. It's verbose name to rerun her server. Okay, that looks okay. Let's go ahead and run. Python managed up. I make migrations cause it's always good to check to see if you have migrations. And sure enough, we have altered the field type on here. Python managed a pie, migrate and let's rerun her server. And now when we refresh our page, instead of all of these choices, we're going to have the regular choices that we have defined. We've basically taken all of the choices that leg will give us and we said No, let's Let's limit that to just four different types. Nothing in her temple is going to change because we're already just using these four. And whenever you add a new field in here, that new field type is also going to be single line, multi line email or a U R L Field. And that's that. That is how we limit the form field choices. 37. .webp Image Support: Wempe is an image type that Google's been pushing for a few years now. It's apparently a lossless high compression image format. Now the thing is, it's not supported by every single browser in the world, not like the standard J. Peg or a PNG file, but I'm sure the sport will get there eventually. The nice thing is, wait till 2.7 and forward. It supports Web P images out of the box, so let's go ahead. Let's open up a stream fields. Let's go to flex models. We have a large image in here, and we're going to edit this template now to create a Web P image is super, super easy. Really. All you have to do is after year, your image dimensions that you want to crop two or fill, too. You really just put format dash Webby. That's it. And it will create a Web P image for you. It will sign it to the image variable, so you don't really need to change anything else Now. I'm not going to do that. I'm actually copy this over and to format when P as Wempe image and we're going to look at this u R L So went the image dodgy. Well, now I just happen to know that the about us page has an image on it already. So I'm gonna refresh this page and we can see it's produced a Web p U R L for us, and I'm going to go to local host 8000. And there it is. This is our Web P image. It looks, honestly, the exact same as our regular JPEG image. Now, when you're using your Web, the image generally, I believe we put them inside of the picture tag and you add a source. So you got a source in here. Source set is equal to, and then you do your Web P image. That's the wrong one with the image that you are. L tape is equal to image slash Web P, and that's it. So now we've got a picture tag trying to use Web P and falls back to a regular image. Now, as an experiment, I'm going to view the source code here, and I can see that there's ah, Web P image and there's also a JPEG image. So I'm going to download these files. All right, so I just downloaded those files, and here we can actually see. One is format Web P, and the other one is just a regular J peg. The Web P is more than 50% smaller than the JPEG. Our Web P images only 32 kilobytes, and the J peg is 81 kilobytes. Both are fairly small images, regardless, but nonetheless it is still significantly smaller. So there are some pretty big performance gains, especially with heavy image based websites. And as a quick recap, Really, all you have to do is add format dash Web P behind your image dimensions, and that's it. So for the rest of this project, you can go through all the other source code and add Web P in there as well. 38. Other Ways to Learn Wagtail: Let's talk about a couple tricks to learning Wag Tail really fast. So you've been going through this whole course and great job on getting through this whole course, by the way, because there's a lot of content to get through. But there's a faster way. It's the way that I learned. And it's the way that a lot of other people I've heard, they've managed to learn wag, tail and jangle very, very quickly. So first things first. You have the docks, the docks, you know the dogs are pretty good, but like any documentation, it's really only going to get you so far. Until you actually experience some of the code, you're not really going to memorize a lot of it. And much like this course, there's only so much content that's people like myself can generate toe help. You learn really anything. Now, the way I learned wayto was by diving into the source code, and you can do that with your editor. But I just downloaded it straight from Get Hub. I just clone the repo and, for instance, I went to blocks that pie and I just right click, go to definition or go and find the code right inside of the get hub repository. And then I would just go see what all of these air made up of his made up of a block. So what is a block? And then I go in here and then I would just read the code. And if I didn't know something like what medical ass is equal to, I would go on quickly. Give that Ah, little Google. For the most part, everything is very, very small and modular and easy to learn. But like a block, every block has a name. Every block has a creation counter. Well, that's interesting. It comes with a default class with no label. The default icon for every block for every struck block is a placeholder has a class name. We haven't even looked at class name. We haven't even looked at group either. So those are two things that you can add to every single struck block. Now, that might be a little overkill here, So let's go to a simpler example. Let's go to the home page and I'm just going to look at the page itself. The page class, This is in my rocket, man. ven of live python 3.7. Like packages Wag Tail core models dot Pie and the page class, I can see that the page class is made up of an abstract page index dot index and a cluster herbal model. I can also see that every page has a title. A draft title, a slug content type, which is a foreign key to a content. A live status has unpublished changes. A UL path, a owner s CEO title showing menus default showing Manu search description All this stuff and this just goes on and on and on. And then we can see all the default search fields so we can at our own search fields if we wanted to. That's not really a thing that we dove into Ah, in this course. But there they are. Search field, so you can always add your own fields to this list of search fields. Max count. We talked about the max count. We very lightly sort of talked about Max Count per parent. Here are panels, content panels, promote panels and settings panels by default. There's nothing in there. Then there's the in it. Well, what does this actually doing so this is a really, really good way of learning how way tell works Now. Granted, there's a lot of coat. Wag Tail has been actively developed for several years now, so there's a lot of code to get through, and that's not for everybody. Some other people like to experiment, such as myself. I like to experiment. I like to get into the code and see what is available instead of just looking at source code all the time. You as a reading source code is helpful. But again, I'm not actually getting my hands dirty, and I can't always see in my mind what things are doing. So what I like to do is Python managed up I shell Plus. Now this comes with a different package, so this doesn't come with regular shell. So Django always comes with Shell so we can see a regular shell here, and it's, you know, pretty boring. But Shell plus is a different thing that comes with a different package. So let's quickly install. This hour is going to quickly look up Django install Shell Plus, and that comes with Django extensions, and all we have to do is run this command your Pip, install Django extensions and then in my project because it is jangle based. We're going to want to run or add rather, Django underscore extensions. Let's open up our terminal. I just cleared that off. Let's run shell, plus once more and here we go. We're in Shell Plus now. The difference is you see all the screen stuff that's automatically loaded in here for you , so you don't have to worry about, like importing wag tail pages or sites or tags or anything. It's just available to you. So, for instance, we can get every single wait till page by taping page dot objects dot all, and that gives us a query set. We can see. That's a page crease out of all the different pages, including our route. But let's go ahead and let's get our home page. So page Home Variable Home is equal to home page dot objects dot First, we only have one page, so we're just going to get the 1st 1 and we have a home page in your then I do home page dot and I hit Tab depending on your operating system, or you're terminally might have to hit tab twice. But look at all these different options, and this is how I enjoyed learning what everything does. So home dot does not exist. What we know that comes from Django home dot body. That is our stream fields. So we can now go. What do the stream fields look like? Look at that home dot body dot What else would his body give us? Count Index Rod text is lazy Stream field, child stream, block stream data render as block. We have all sorts of things in here. Let's do count and it's not a method, all right, we've read account. Does that come with Anything else comes with all sorts of stuff. So now we can really start to get into this. That was active sort of a bad example because it was a little bit too complex, but we can see the home Page I d is three the home dot live. It's true. Home page is live. What is the home page slug? It's just home and you can find other things in here that are really, really helpful as well. So all the things we saw in the source code, it's all in here showing menus showing menus default slug. We looked at that stuff already. We can get the full UL. We can get the lead text. That's a custom field. We can get the U R L The Ural Path, the i D. We can run any of its methods, like get contacts. We could try to run some of those methods, although we might need toe mock some extra data in there, especially if you're trying to run a test on a page. We can see if the pages locked. We can try to move it. We can see the owner of the owner's i D. We can see all sorts of stuff in here. So if you're really interested in what comes out of the box with Wag Tail, honestly, just spin up shell, plus install Django extensions, install shell plus and just tinker around. We can also do things like getting our menu items. So let's do menu is equal to men. You dot objects dot all know we only have one menu. Let's do the 1st 1 So we've got a menu. We know that this is our underscore. Underscore string underscore underscore. That's the title we can now do menu dot and we see all sorts of stuff in here. But the one that we added was menu items with you menu underscore items dot all and it's a query set. We can look at our testimonials. Testimonials is going to be Let's do t is equal to testimonials. And I just did tab for Auto complete. We have two testimonials in here. One Biota, one by Chewbacca. T one is equal to the 1st 1 That's our first testimonial. T one dot What do we have in here? We've got a quote. What is the regular quotes just to quote text, do or do not? There is no try and the attribution is from Yoda. So now we can see what our data is actually presenting us, and this is really, really useful when you're heading things like in our service models, our service listing pages, trying to grab all of our service pages and then a sign of variable and put that into our template called services. So let's go ahead and see what this returns. It gives us cleaning and shining. And so now we know if we were to test this out in her show. That's whenever we type service pay shot objects dot live dot public. With the current pages that we have, it's only going to give us the two options. And so if you actually go and check out our page and you see three cards are only one card is showing up or one services showing up, it's likely there's something else going on because we know for a fact there's two here so wired to services, not showing up on a template. And so now we're starting to get into some debugging and stuff. Lastly, we have the actual repo itself so we can see everything that's going into master. We can go in here. Let's let's look at version 2.7 and we can go in here and we can see all sorts of stuff. So let's go into Wag Tail. Let's go into core and let's look at models and in here we can see that this is, ah, fair sized page. We have ah site. We've got a site manager and screwing all the way down all the way down abstract page and look, there's our page itself, and so now we can read all the source code outside of our editor in our browser. And again, this is everything that comes with a default. Wait till page. So now we've got another way of learning wayto the other way to learn wayto is honestly go to wait till dot io slash slack and we will see this page load up. Join the slack workspace for why tell Come join us on slack. There's a whole community we've gotta support channel Come ask your questions in there as well. And so other than taking this course, all I can really say is open up your shell plus and just start messing around with things. Honestly, the worst case scenario is you know, you break something in your database and you have to create a few pages over again, for this is a really, really good way of learning how wait till works and what features that comes with and what you can do with wayto 39. Global Site Settings: site settings are options or data points, if you will, that you can set on your site and they're globally available. So unlike a page when we set a particular field on a page and we really only have access to that on that page template a wait till site setting allows you to access that site setting from anywhere now. A good example of this is actually an hour footer where we have hours and stuff actually know what I'm just I'm going to spend up the server and I'll show you so the example is in her foot, or we've got contact us hours and social media settings. These air currently all uncontrolled, even even this section here, the Global Sea today, with some text and a link that is all currently uncontrolled. That's hard coded stuff. There's no way we can change that. Site settings allow us to change that because it's available in every single page. Well, we don't want to do extra work for every single page, so we can register a site setting a custom site setting, and it will just be available to us everywhere. So let's go ahead and create a new app in here we're going to do Python managed up. I start up, We call it site, underscores settings and we run our server just like we've done every other time. Let's go to know that's not the right they stop. I will go to base dot pie and right up here we're going to add state settings. Go ahead, say that. Enclose it. And in our explorer here, we can see we've got a new folder called Site Settings. We don't interviews. Right there's gonna handle that for us. We don't need tests. Tests are mostly written for Wait till already. Plus, we're not doing anything crazy. We don't need admin, DuPuy. But we're going to keep, innit? APS and models now, essentially, what we're going to do in our site settings models up I file is we're going to create some sort of class like social media settings. It's going toe inherit something from Wag Tail called a base setting. And then we're going to give it field so we could give it Facebook. We could give it Twitter, YouTube and instagram. I think that's all we have, right? Facebook, Twitter, instagram and YouTube. Yep. And each one of these is going to be a You are all field, so models that you are l Field and Blank is equal to. Let's put this on multiple lines. Blank is equal to true and the help text is going to be Enter your blank, blank blank you are L. Now we got to do a little extra work here because this one's going to say Facebook. Enter your Facebook, you Earl, and this one's going to be enter your Twitter you are on this one's going to be YouTube and if you haven't guessed it, that's what is going to be instagram. But again, because we're doing this in wait till we want to expose these. So we we need some panels now in here. We're going to say at a fuel panel for Facebook, and let's copy that a few times. What else do we got in there? We have Twitter. We have YouTube and we have instagram. Now, at this point, we need to make sure models is imported. Field panel is imported and base setting is imported. Plus, we also have to do one more thing to actually register this as a setting because right now it's just a class. And if wait till was to register, every class is a setting. We would have hundreds, maybe thousands, of different settings inside of wait till. So let's go ahead and do from django dot db import models. That's our standard models. We have a base setting, and here's what's due from wag tail dot con Trib dot settings dot models import base setting. We're also going to want to import the register setting decorator, and we can apply that right away. A thing to keep an eye on years. Whenever you import Con Trib, you're going to want to make sure that that's enabled again. That was the wrong based on pie file. So what we're looking for in here is Wag Tail Contra settings, and we don't actually see it in here. So let's go ahead and add our settings. Let's just throw it in here wayto dot con Trib dot settings. All right, then you can save that and close it, and now we have access to wait till contract settings and the models file that comes inside of it. Lastly, we need to import our field panels because without those fueled panels, while this file is just going to complain, or rather, our terminals going to complain about this file, although it's not, and that is quite suspicious. Oh, there we go. Just had a hit. Save a gun from Wag tail dot at men dot at it handlers import field panel terminal and is happy. Let's go into our way tell at men, and I'm going to leave this page. And if we go into our settings here, we can actually see that we have social media settings Now. This is going to give us an expected error because we're trying to store data inside of our database, but we haven't ran migrations yet, so let's go back to our server here and let's run Python managed up high, make migrations, and that's going to make our python migration settings, not settings. Instructions that the word I'm looking for Python managed up. I migrate, going to run those instructions and alter our database a little bit. And then re runner server. Let's refresh our page here and let's see what this gives us. So now if we go into her settings, social media settings, it brings us to this page where we have a Facebook, Twitter, YouTube and Instagram. You are l. So I just filled out those year Els and I'm going to click. Save cool now in our Footer. We're going to have to actually use these girls, and we're going to have to do a little bit of checking to see if those are actually set. So let's open up footer dot html And let's close that sidebar and let's go look for social media stuff here. So we have a link to Facebook now to access the site setting is super easy. We just type settings dot sight underscore settings. That's the name of our app. So if you named your app something different, it's going to be whatever you named your app and then it's going to be social media settings noticed that the casing changed here. This is the name of the class, and then we do Facebook because that's the name of the field. So let's save that and give us a quick little refresh and hover over Facebook, and we see it didn't do nothing. Now the reason we don't see anything in there is because again, that's the wrong based on pie order, my my editor is doing that to me is even though we enabled wait till contract settings. Well, that's nice, but we just enabled it. In order for us to actually get the settings on every single page, we need to add something to our context processes. So we needed to be available on every page. So we've got context process or somewhere in here. So templates, options, context, processors and the one we want to enable here is Wait till dot con Trib dot settings and then the context processors don't settings. And I'm hoping I don't have any typos in there. That looks okay. Let's refresh this page. And now this will turn into a link for us and look at that at the bottom left there it says facebook dot com, and if I click, it goes to facebook dot com. So now we can do this with all the other links as well. So this one could be Twitter. So again, settings and then the name of your app. So this one's called site settings. The name of the class social media settings. That's what we called him. This one is the Twitter field, and this one is the Instagram field, and this one is the YouTube field YouTube. And let's refresh, make sure all of these are working as expected. So that's Facebook, Twitter, insta and YouTube perfect. But now what happens if one of them is not filled out? We need to actually write an if condition. And if condition in here and this could get pretty ugly now, there are ways around this, but I'm going to keep this simple and simply check just for this particular field. So I'm going to say if and this looks pretty long so if that exists in debt that and say and if and I'm going to do that with all the other ones as well. All right, So I just added those settings in there. And just as a test, let's go ahead and let's get rid of Instagram. So we have three social media fields, but Instagram is empty, so it's refreshes and instead should go away and just like that, instead goes away Perfect. Now let's go ahead and add hours and our contact us Now. This is super easy as well. It's pretty much the exact same thing. Is this So what we're going to do is create another class. It's called contact settings, and again this is going to be a base setting. And before we do anything, let's register this. So let's register this setting register setting. There we go and let's add one field in your contact is equal to a rich text field, and we want the features to be link on Lee so people can link but no bowls. No title is nothing like that. And let's go ahead and also say that this could be blank. Blank is equal to true. Now let's make sure that no one could be true to, I guess. And just like every other time, we need to make sure we have panels and years old. Panels are equal to field panel contact, and now we need to import a rich text field from widow dot core dot Fields import rich text field, and we're going to have to run migrations again. So python managed by make migrations breath on managed up high migrate Django Admin starts over, runs over as refresh our settings here, and we'll see once the page refreshes and I opened up. This menu has ah contact settings. And now we can add some contact settings in here so we can say something along the lines of Contact me. The email, I guess. And it's just going to be Caleb at learn. Wait, tell dot com. Let's go ahead and turn this into an email link and save now again. That's not going to show up in her footer again against Django in Wayto and Python and no other programming languages really intelligent enough to know exactly what's going on in your brain so it doesn't make any assumptions. But what that allows us to do is have full control. So where it says, contact us, we can now say site. Nope, that's wrong. Settings that site settings dot the field name. And because this is rich text, we put pipe rich text, and that comes from Wag tail core tags. Let's go ahead and refresh your page and let's see what this looks like. Not bad. Not terrible. Probably get rid of the paragraph tag in there, but that's on two lines instead of two paragraphs. Yeah, that looks okay. And that's a link, and lastly, I'm actually just going to copy and paste his whole one over for hours as well, and instead of contact settings is going to be ours. Settings and instead of contact is going to be hours and instead of link, it has no features, but it's allowed to have new paragraph, so that's why we're putting it in there. It also love to have new lines. So I guess paragraphs and your loans. That's why we're putting it in there. Let's say that Let's do this all in one line. Let's do python managed up. I make migrations and end python managed a pie, migrate and end python managed up by run. So fever on local host Port 8000 three commands one line to rule them all. Okay, let's refresh. And now we have our settings. You know, we put some hours in here so we could say something like Monday to Friday. Riding was a funny way to spell Friday. Ah, 9 a.m. 25 PM And just like before, we're gonna add that in there, so settings dot site settings dot contact settings. Nope, that's not right. It's called our settings, and the field is called hours dot hours. This is also rich text. Okay, that's coming together. Now we have one more section in here, and I'm actually not going to cover it because it's very, very similar to what we've done. And we've already covered a lot of these subjects pretty much to death at this point. But we could add another site setting for this text in here. Another sites adding, for this tax in here, a sight setting for it page to link to you. So you use a page chooser panel, and then you have just changed that Page two be in your template. It'd be like settings dot site settings dot c t a settings dot link page or whatever you want to call that field dot u R l If you're interested in how that looks, you can always check out the source code. But I'm not going to keep you any longer on this video because it's pretty much everything we've covered already. 40. Changing the Wagtail Admin Logo: is. Sometimes we want our website admin to feel a little more branded and by default, way Tell comes with its own branding, which is totally cool. But one of the biggest things weaken Dio to swap out with that branding is to just swap out this logo and wayto allows us to do that really easily. So really, we just need to create a new template file called Based on HTML. But it's going to live in a particular folder, and then we're going to override a block called the Branding Block. And generally I just put like an SPG or something in there, and it overwrites it. So it's Let's go ahead and give this Ah, quick little demo. So I'm gonna close that up and in my templates folder. So rocketman templates. I'm going to create a new filing here, and the first folder actually is going to be called Wag telling men, and the file is going to be called base dot html. Now this needs to extend from a particular file. So instead of extending from our based on HTML, it's going to extend from the way Tell and Men slash based on HTML and then we have a blocking here called the branding block. No branding logo. I believe it's called. Yeah, we'll find out if I'm wrong, but there's nothing in here. So we're basically just getting rid of the bird. And sure enough, there's no more bird Now. We could also load in static, and we could check to see what files we have in our static files. So it's go static images and we have logo dot PNG. And I don't think I actually have a logo dot s v g in here, although you just paste in the SPG itself. So let's at an image, and that's going to be static images logo dot PNG on the altar is just going to be Rocket Man logo. And where I got that image from a static images and then logo dot PNG static images logo dot PNG. Refresh us and let's see if this looks terrible. Yeah, that looks pretty bad. You'll probably want to use a different image. I think this would look a lot better if I actually had a white logo that, you know, looked half decent. But I'm just using the logo from the page or the front end of the website, so I'm just using this one. But if you have a logo or your client has a logo that you want to put in there, it's as easy as that. Now, what I'm going to do is comment this out. I'm going to keep the code in here. But I'm gonna comment this out. So if you ever need a reference this in the source code, you have access to that right away. 41. Adding Caching: All right, let's talk about a really, really big subject cashing Now. Cashing is one of the fastest ways to speed up your website without needing Teoh. Add additional hardware to your server without having Teoh really do anything to increase performance. Now there are tons of different types of cashing. But to keep this simple and because I don't really know what services you are going to be using, maybe it's meme cash, right? It's elastic cash, etcetera, etcetera. I don't really know. We're just going to stick with a simple template fragment cashing now. Template cashing allows you to cash a piece of your template once it's been processed for the first time. So, for example, our header up here we could cash that, and it would be the exact same on every single page. We could cash our banner, but more importantly, we would look at cashing sections like this. There might be a number of queries on here that weaken reduce on her page by cashing this, and so we could cash this entire section or this entire section with cards, or we could cash them together. We could cash this entire page if we wanted to. We could do all sorts of things with template fragment cashing. So taking a look at her home page, let's see how many queries this has. This has 36 SQL query, so it's actually not bad at all. But the thing is, SQL queries are very slow when compared to everything else. It's kind of like, you know, when you have to drive to work and then maybe you get to work and you realize, Oh, no, I forgot my laptop at home. So now you have to drive all the way home, grab your laptop, drive all the way back to work, and then you realize, Oh, I forgot my laptop charger. So you drive all the way home, grab your laptop charger, come all the way back to work. It's really, really slow in the sense that it makes multiple trips like that, from your website to this to the database and then back to the website and back and forth and back and forth. So if we can cash heavy query areas, we can actually speed up our website dramatically. And the idea is if we can really just remove queries from every single page request and cash it with something like in memory cashing That would be the best, but it's pretty much impossible, but we can really dramatically reduce this number. Now again, 36 queries is pretty good. If you're upwards 200 you're starting to look a little iffy. If you're over 200 I would definitely say Optimize Now for this, we're going to be using template cashing, which means a section of your template is going to be processed. So, like this section is going to be processed. And then once it's processed, that first time is going to save it in a dot D J cache file. So let's ah, let's jump into this. We'll close that down and let's open up, dev dot pie. We're going to do our cashing set up here, so it's really just in our local development built, and then once we're done with that, we're going to move it over to our production dot pie file. But Deb type I is really just to make sure that things were working the way we expect it to work. So the first thing I'm going to do appear is from base import. All that should already be in there. But I'm also going to do not from just imports os and put that at the top. Now I've got a secret key. Loud Coast allowed host email back in, installed APS, middleware internal I P s. But one thing I don't have in here are forms of cashing. And I'm wondering, does this come in production dot pie new. Does that come in? Based on pie? Does it come in the proper base? Done pi setting cash? No, not by default. So we're going to add our first cashier. So this one's going to be called default. We're going to give it a back end of django dot core dot cash Don't back ends. This is going to get very long, by the way, dot file based dot file based cash and then we're going to give this a location so the location is going to be somewhere. We don't know where this location quite is yet, but generally it's going to be in some sort of dot cache folder. So for now, let's say dot cash And what I actually want to do is put it in the current working directory. So ever managed up eyes running. I sort of wanted to create a dot cache folder right where that is Now. I don't know where that's going to be on any given server, so we need to do a little thing here where we just add a very ball called C W D. And that does not exist yet. But C W A. D is going to be equal to os dot and get current working directory, and then we're just going to append dot cash to it. And what I'm going to do here is just make sure I don't have a type of back and Django core cash back INS file base do file based cash. There we go. That is very prone to me, making a typo that's all saved servers running. And when I hit my home page, it doesn't know what to cash yet, so it's not going to do anything. But if I open up my editor or if you're in your terminal, you can do LS or aider, and you can see in here I have a big file called 911502 blah blah, blah, blah, blah, blah, blah nine F a dot d j Cash and it's a file that's can't even be read. But it is a cache file. So now let's go ahead and add a little bit of cashing. We're going to do this by opening up our home page, home page dot html and where were loading our tags. Let's add cash in here. I mean, close that side bar down. And now with this cash tag is going to look like is we use the word cash How long we want this to be cashed for in seconds and then a name. So, you know, page cash, something like that and then optional unique fields to make it a little more unique so we could call every page page cash for every section could be called page cash. But this one could have a unique idea of 123 And so we've got an opening cash block and an end cash block just like that, and that's how it works. So we're going to go ahead and we're going to cash our stream fields on the home page and we're going to run a little experiment here, so we're going to cash this for Ah, what is 30 days? Something like if I remember 259200 zero That many seconds, Somewhere on there, we're going to call this home page stream fields, and we're going to give it a page, I d just to make it a little bit more unique and do and cash. Okay, I've saved that. And now, as as ah, little experiment, I'm not going to reload this page. I'm going to go into wait till edit my home page. I'm going to change some text on the first section here. So was your rocket recently in space? Get it cleaned and this is going to be the cached title. And then watch this. This is going to save. Refresh the page. This is now going to create a new cash file for me. It'll say the cash to title, and I'm going to get rid of all of our stream fields so nothing should show up anymore. And when I refresh this page, everything still shows up because it's been cash as a template fragment. Django now says, Oh, look for cash. Make sure that it's less than this many seconds old. Its name is home page streams, and it has a unique idea of whatever the page ideas. It doesn't matter that there's nothing in here anymore, it says. Basically, I'm looking for a file called this and it makes a weird little hash out of it. So let's take a look at that hash. It's this top one here. So it makes a hash over the file name and says, Yep, that file certainly does exist, so I'm not going to render anything that's between the cash and the end cash tag. I'm just going to render what was ever in this file, and so it stores it in another file, and now there are no extra queries. So let's open this up and we see we have 26 queries in here. I actually think it was 36 originally was 36 queries, and now there's only 26 queries. We could reduce the number of queries by cashing our header and our footer as well are. Foot is probably pretty quickly, heavy as well. Now I'm going to undo that and we're going to run into a problem now because this page is cashed. I'm going to change the title here to be and uncashed stream field, and I'm going to preview this. It still shows me the old one. The cash title. This one should say an uncashed title right now. Is that what I wrote? An uncashed stream field? But it still says the old stuff so pretty view is now broken because of cashing. But what we can do in our template is we can detect if this is a preview or not. So we can say, if not request is preview and this is going to be alive. Otherwise, this is a preview, and this is going to be a preview, and we can even check this out as well. So let's do this. H one. This is live and let's make another H one that says preview. And let's refresh our page here. Now, this is outside of the cashing area. So this is going to work. This is live. And this one when I refreshed the preview says preview. So now what we can do is we can take this cash section and only apply it when the pages actually live. And in the preview, we don't want to apply any cashing. So we're going to take this four block or this four loop, and we're going to apply it in here. So basically, if this is a live page, render this section out first. And once it's done rendering, throw it into a cache file and don't ever worry about it again until this time runs out. Otherwise, if it is a preview, you can see your previews in here. There's no cashing whatsoever. And if I click on preview again, this no says an uncashed stream field. And if I go back to the life page and refresh, this still says the old stuff. So the preview is now working. Now we're running into one more issue, and this is pretty much what cashing is in a nutshell. Speeds of your website, but you have run into some issues here is I'm going to change this to a brand new title, and uh huh at the preview ones, I'm gonna close that when I refresh the live page here again, nothing has changed. That's because wait till and Django neither of them know that this content in here is actually changed. All it says is, Oh, when I'm rendering the template, I'm gonna look for this file, Whatever that file name is. If it exists, throw it in here. It's not checking to see if this has changed. That would defeat the purpose of cashing. So let's go ahead and remove cashing every time the page gets saved. So I am on home models dot pie and at the very bottom here I am going to create a new method called Save. Now this comes with the page already, and we're simply going to override it. Arts and coir eggs. And once everything is done, we're just going to run Whatever. Wait till wants us to run. So save and we're gonna pass in our dogs and our cords. Now, in here is where we need to somehow get rid of our cash, the page cash that already exists. We need to get rid of it. No, Django comes with really nice feature that allows us to make the template fragment key and then delete it. So we're going to make this key with a function called make template fragment key. Now, this is not imported yet, but we will import it in just a moment. And the first parameter is going to be this title right here. Home page streams. So we give that as a string, and the second parameter is going to be whatever this page ideas now, at this point in time, we don't really know what that page ideas, but it doesn't really matter because we know that I d is a field on the home page, so we can simply do self dot i d. Because this is object oriented programming. We get self doubt I d. And I d comes from Page itself actually comes from a jungle model. So now we have a key in there, and all we have to do is run cash, delete past the key, and then it will save the page. And then once the pages loaded once again, it will re cash it for us. Now we need to make sure that we have make template fragment key imported and also cash. So let's go to the top of our page here and let's do from django dot core dot cash import cash. And let's also do from django dot core dot cash dot you tills import, make template fragment and let's see no complaints so far in the terminal. And let's just verify that we still have this problem going on. So we have a section in here called a brand new title, and when I refresh this page, it still shows the cash one. Let's go ahead and publish It says it is not in terrible. So I have a problem somewhere. Self doubt, i D. And the reason that happened is because you can actually pass multiple parameters to your cash. And this is supposed to be a list, and this is why we do testing. So let's refresh the page. Fair fire content. Is there a brand new title click publish? Wait for it to save. And now when we refresh this page, this is going to change a brand new title. This page has 36 queries because it's executing all the queries in all of our different stream fields. But if we refresh, there's now only 26 queries now we can basically do the exact same thing to our header and a footer and all sorts of things, but I think we're going to save that for the next lesson because it's not quite the same is very, very similar. But it's not quite the same. We have to add the save method to our menu and Teoh bunch of our site settings, so we're going to do that in the next lesson. 42. Caching the Navigation and Footer: Okay, Dokey. In our last video, we added template cashing to our home page stream field in every time we saved the page, it deleted the old cash. And then when we reloaded the page, it would recreate the cash. We also made sure that whenever we click preview, we're not getting the cached version of the page. And that reduced our queries by tens. We used to have 36 queries. We now have 26 queries, but let's go ahead and cash our header and our footer and try to reduce those cruelties even more. Also, if we take a look at the CPU time, I believe this actually was originally somewhere around 850 milliseconds. Granted, this is not a dedicated computer, and I'm also recording What not so it's gonna take a little longer, but I think we could get that number to drop quite a bit as well. So first things first. Let's go ahead and open up header dot html and we want to add the cashing template tag in here. And what do we want to cash on our header? Is there anything dynamic in your? No, not really. We're not even using active classes on our links. So that's actually OK. We could probably cash this entire header. So in her header file, we could just add cash up here if we wanted to. But actually, I don't really like doing that. And I just want to cash just this little fragment here. Just our for loop. That's the expensive part. So we're going to add some cash for 2592000 I think that's roughly one month in seconds. Let's do 30 days times 24 hours times 60 times 60. Yet there does. 2592 Triple zero Death nailed it. Now I'm just going to call this one site header on the home page. We gave us an additional optional parameter, this one we're not going to because it's just going to be called the site header and Let's and cash and let's open up menus models dot pie. And whenever we save our menu, that's where are you this guy right here? Whenever we go into a menu and we save this particular object of this class, we're going to bust this cash. We're going to delete the cache file the template fragment, and we're going to allow it to create a new one where the next person hits our website. So the bottom, I'm simply going to say Death saved self. Let's pass in some quarters eggs and assuming everything is good at the end of this, let's run super save and passing those cards as well. Those keyword arguments. And that's just going to run the save method on the Cluster Herbal model class. And now we need to create a key. So it's create a key, and this key is called or executing. Rather, this key is executing make template fragment key, and this one only has the one parameter so really need to put the one in there is called Site Header. And then let's do cash dots delete the key. And on our home page, we imported cash and make template fragment key. So I'm literally just going to the home page at this point because I've already written it . I'm going to copy it and in my menus models up. I was going to paste it in there. That looks good, and now we can tell if this is really working because there's going to be extra queries that are going to be taken off. So the first time you hit this page, it's not going to save any queries. As you can see, it's still 26 Caries or that 28 queries. Now there should be some template cashing in here. Now when I refresh the page again, I'm down to 23 queries. So that saved us five. So not a really big performance increased their I suspect a lot of that is actually coming from the footer where we're going to see the biggest gains. So let's ah, let's do that one as well. So now we can go to our footer dot html because we're adding cash. We need to add that cash template tag. And we know we saved five queries immediately from just cashing this four loop that was in the header. This is basically the exact same four loop with This one is in the footer. Now we can add cashing. Here's a cash 2592000 You could set that number to be significantly lower, significantly higher. If you want. We're going to call this one site footer and and cash and Let's go ahead and open up our menus models. Now, every time we save this menu, we needed to break the cash up here. Bust the header, cash. But because this is also using the exact same menu in the Footer. We wanted to bust this cash as well. So let's go ahead and weaken. Copy this instead of sight header. We'll call it site footer and that's right footer matches, matches, matches, matches this name right here. Let's go ahead. Refresh our page. We'll see that the queries don't change when we got two more in there but not too worried about two queries. Let's refresh in. Boom. It drops down to 20 and it's consistently 20 now, which is really nice. That's CPU. Time is dropping as well. A drop 200 milliseconds. Now let's go ahead and cash are contact us out hours and our social media settings so we can close the menus model Stop I file, and we're just going to work inside of the footer and the site setting so it's do site settings. Models up high is the one we need to open here and before anything already have this in my clipboard so I'm going to paste my imports in there. So I've got cash and I've got make template fragments key. So every time we save one of these settings, let's go ahead and break the cash. Now we haven't said the cash in the template yet, but we can set it in here and then set it in the template afterwards. Says to Deaf Save. This is going to take self any arguments and any keyword arguments that we pass in there and just like before, If everything is good, run super dot save. Let's pass in the guards. Let's pass in the cards and it's good to go now. We need to set the key in here so the key is going to be make simply fragment key. Which class are we on your contact settings? Let's call it Twitter. Contact settings, A new cash dot delete key. Now that we have that, we can copy that that whole section because it's going to be the same. If it works on one base setting, it will work on all the base settings. And so this one is our hours setting, So footer hours, settings, social media sightings. Let's go down to the very bottom here. We've got our social media settings, right? Just social media sightings. Yep, footer. So called social settings. And then we've got her foot or c t a settings. And let's do the same thing down here. Footer si ta settings. Now just because these template keys don't exist doesn't mean it's going to break your site . So currently, those sections are not cashed on her site. But if we go to the foot or c t A Settings and I click save, there was no template fragment key to delete. So nothing happened. Let's open up footer dot html again. And, ah, let's do all these different sections. I'm gonna make this just a tad smaller because this is a fairly large file on and I'm going to. I'm going to cash this entire section here, and this one will relate to our foot or C T A settings or inner temple, Cash Foot or C T. A Settings. So let's have some cash in here. Cash. I'm going to cash this for a month as well. Call it Flooded foot or C T. A settings and down here and cash. Our links are already cash. That's groovy. We can cash are contact settings or contact us. Which one was that? Um, but, um Twitter contact settings. So this is going to save us a query as well, although to be honest, it's not really worth it, So I'm not going to do this one now. The reason I say that is because, for instance, this whole links section of the bottom left that's cashed and how template cashing works is it takes your fragment, and it's still stores it as one single SQL query. It has to hit the database, see what it's looking for, which filed, supposed to look for, Come back, check to see if that file exists. If it does exist, use it. So because there's only going to be the one SQL query in here in the one SQL query in here . Honestly, that's not really worth it. I'm going to leave the template fragment key and a cash deleting inside of the models, but that one's not really worth it. Ah, bigger one that would be worth it here would be all over social settings, because for each setting here, it has to look up Facebook, Twitter, instagram and YouTube. So let's go ahead and just cash all of these. It's to cash 2592000 and we're going to call this Social Footer settings, and I like to make sure that my code is always somewhat nicely indented and cash. Let's go ahead, refresh our page. And sure, we gained two more queries for just a moment as it created the cash and now we drop down to 17 and this originally was 850 milliseconds. At the beginning of this lesson, it was 650 ish milliseconds, and it's now down to 400 milliseconds. So that's that is really, really good. And I am willing to bet that because we're logged in, we have a few extra queries to authenticate our user object to make sure we are actually an end men and things like that. But if we open up incognito mode here, yeah, sure enough, you can see 10 queries took to milliseconds and the whole site took 200 milliseconds 125 milliseconds. So those are some pretty good gains from 850 milliseconds a to 25 from 36 queries to 10 queries and lastly, as one little recap. Really, all we did was we said, Hey, there's a save method on our base setting. We created a template fragment key. And so we said, This is the only setting that we have to match on our template. So we've got foot or social setting, and if we go back to footer here cash for about a month and that's where we find this text here, foot or social settings, then we take that key and we simply delete it. And then we continue saving that model as if nothing ever happened. And now we have template cashing all over the place. Now in the source code in the final source code, you're going to see a bunch of template cashing that I'll be adding all over the place right now. It's just the header, the footer and the home page stream fields, I believe, but I'm going to add it to other places as well. So you're going to see a little more of that. All right. Lastly, if you ever want to disable your cashing, you can always common that out. Or what I like to do is I just take that, knowing that it's already working as expected, Go over to production dot pie, and I'm just going to paste that in there. And I want to make sure I also have that OS file for that OS import. Rather. And now I know when I launched this Web site in production on a server that I'm going to be using the production up by Settings file. And so this is going to use Temperley cashing by default. 43. Adding a Sitemap: site maps help search engines. Find all of your pages. Wait A has a site map feature, but it's not actually enabled by default. And in this lesson, we're going to enable site maps and create a new site. Map. U R L So if we open up our website, local host 8000 slash site map dot XML This is usually where a site map lives, but in this case, there isn't one. So what we need to do here is open up our base dot pie. So what we need to do here is we need to Where are you? Check are installed. APS. We need to get our insult APs to include rectal dot con Trib that site maps Make sure that's not already in there. And let's also add the jangle one django dot con Trib Dad site maps and then we're going to open up. Are you RL's dot pie file, which we I think we've only actually touched once in this entire course, and there we go. We can see that Django admin that I've been talking about every now and then we haven't even looked at it so honestly, if you want to you can totally just get rid of it. So by and in here, I'm going to say from wagged Oh, don't con Trib dot site maps dot views important site map and then above this catch all we want to add you are Oh, site map dot xml Yes, we could make it read, Jack. So it looks a little more like the rest of our our code here, So we could do you are l site map dot xml. And instead of this stuff, we're simply going to say site now, terminals not complaining at all. And I'm going to go back to local host support 8000 slash site map dot xml And you know, I've got home page services cleaning page, shining page about us and contact. So now I have a site map, and when you're launching your website, you can submit your site map to Bing and Google and all the other search engines. Search engines will also by default. Look for site map dot xml on site map dot html, but it's always good to submitted anyways. And really, what this does is any page that you have a wait till page that live. That's public. It will show up in here 44. Prepping for Launch: now, one of those things that we need to do is create a secret key. And we need to edit our allowed hosts and said our databases pretty much for the most part , we're going to be working inside of our production dot pie file. So in here, we need to set our secret key is supposed to be something we don't know what that is yet and we need to set our allowed host is equal to a list of I ps domains, things like that. And we might even see it endeavoured up high allowed host? Yep. Is equal to star. We've already got a secret key for our Deb. This is just for local development. We want to set different, a different key for production. And we want to set different allowed hosts more specific allowed hosts for production. So for our secret key, if we just opened up Firefox or whatever browser you like, and I'm just going to go to many web tool dot com slash django secret key generator and create a random key and I'm going to copy that key and just put it into my secret key like that. Now this is not the best method of storing something secret probably shouldn't be kept in a plane string like this. But that is something that you can tackle a little bit later down the road. For now, we're just going to do it this way. And then in our allowed hosts, we're going to need to set a few different hosts in here. So the 1st 1 is going to be local host. Engine X is going to use this on our live server, and I just know that we're going to need that one. The next one is whatever we want our domain to actually be. In my case, I want this website to live at rocketman dot learned wag tail dot com. And the 3rd 1 is going to be our i p address from Digital Ocean or Lin owed, or AWS or wherever you are setting up your server in this course, we're going to using digital ocean, so there's going to be an I P address in there now. We don't know what that I P addresses yet, so I'm going to put a star. And whenever you use this in production, just make sure that you always double check that you remove it once you launch the website . Next. We need to set up our databases in here because if we go into might be indebted up high. Nope. It's probably in based start up I data bases, yet we're using Escalate. Three were just fine for local development. It's fairly fast. It's fairly lightweight. It's a single file, but in production you want something a little more robust and we're going to be using post grass. So we're going to give this a new database in here. Databases is equal to, and we need to give this a default. This is just going to be whatever the regular database default is going to be. And then this is going to come with an engine in all caps, and that's going to be a string of some kind. A name again, all caps That's also a string a user. This is our post dress user. The name was the database name that we're going to be using also a string the password, which we are going to put in a string. But just note that if you can put it in an environment variable later, definitely do that a host, the host is always going to be local host and our port by default, I believe, is 5432 However, we're just going to leave that as a blank string as a default to 5432 So let's fill some of the stuff out. We need an engine. Let's use django dot db dot back ends Dutch post GREss que el Psycho PG, too. And our database name is going to be rocket Man. I know that because that's what I'm going to set it up. As in the next video, you might want to change yours. Mine's got just going to be called Rocket Man and the user. The Post press user that I'm going to use is also going to be called Rocket Man and the password again if you can put this in an environment variable. But I'm just going to throw this in a string for now, just to keep it nice and simple. Because launching website is not always super straightforward. Let's go ahead, give that a safe Now. The next thing we want to do is install Century Century as an ever monitoring program, so whenever your website throws you a 500 air. You don't have to dig around for logs. It will give you the full trace back right away, and it's totally free as well. So let's go to century dot io, and I'm going to quick get started. Accept that, and I'm just going to create a new account here. It's totally free as well. My name is Caleb Tallinn. My email is going to be Caleb at learned wayto dot com. My password is going to be something my organization name is going to be learned. Actually, this one's not going to be learned. Wait till it's going to be Rocket Man. I don't need billing info and do I want updates. No agreed Terms of service. And then when we're done, just click. Continue. All right. And this is basically saying, Hey, welcome to century. This is our on boarding. Yep, I'm ready. It's going to ask what platform are were using. They were using reactor angular layer of L A PHP symphony to Ruby Python. Anything like that, we are using Django. Let's create a project. Now. This is going to tell us exactly what we need to do to get this installed so we go to step three installed a century sdk. So we're going to want to put this into our requirements file just like that, and we want to copy all of this into our production dot pie, and I'm just going to put it all right there. I'm not going to worry about fixing the imports, putting them at the top. You can if you want to. I just like keeping all the century stuff together, and that's what I'm going to do. Just copy and paste us right into production dot pie. Now I'm going to keep this open because the next step is to verify that centuries installed by creating a ul pattern. But if you're using the source code that I've provided, or if you're looking at the source code that I've provided, there's actually an error in there, and we're going to solve that air on its own in the next lesson. One last thing we want to do here is open up our whiskey dot pie file and in here, and you can just see it in the Rocketman folder called whiskey dot Pie. It says Rocketman settings that Deb, it's going to be your project out settings dot Dev, I'm going to change the dot deaf to dot production. And all this is saying is check to see if there is a Django Settings module in our environment. So it's looking for an environment variable. And if there isn't default to this one, and I'm going to save that as well now, before we get into the next video where we actually go ahead and launch this whole website on Digital Ocean, we need to make sure that we have this in a git repository. No, I'm going to leave that up to you to put that into any repository that you won't get Hub get Labit Bucket, anything like that. But we're definitely going to need all of this code in some sort of version control system that we can access on our server because we're going to clone this down and we might need to make a few changes and then push it back up to our version control system such as get hub 45. Website Launch! : all right. The time is now we are going to launch our website on Digital Ocean. Now the steps can largely be replicated on Lynn Owed or AWS or really any other cloud provider. I'm just using digital ocean because I like them. They're nice and simple. They've got a good price points. And did I mention they're nice and simple? I really like that. So if you don't have a digital ocean account, you're going to probably want to create one. Usually they have a promo going on that says, You know, if you sign up, you can get 20 year, $50 sort of as free money. But I already have an account, so I'm going to sign in. All right, so I just have a new project in Digital Ocean called Rocket Man. You can see I've got coating for everybody on there already. I like hosting with Digital Ocean, obviously. So I'm just going to create a brand new droplet here. I'm going to make sure I have you been to 18 selected just a standard plan and I don't want to spend $40 a month. I want to spend $5 a month $40 is better if you have more like you need five terabytes of transfer or 160 gigabytes of SSD disk space or eight gigabytes or four CP use. You know, we don't need that. We need one gig, 25 gigabytes off SST space and a terabyte of transfer. That's good enough for what we're doing with a small website. I don't need block storage or anything like that. I'm just gonna select the one that's closest to me. San Francisco is closest to me. Oddly enough, I'm closer to San Francisco than Toronto, even though I'm in Canada. Ssh Keys. Here's a big one. You're going to want to add your ssh key, so if you don't have one already, you're going to want to add one. This is going to make your life tremendously easier. It's the same ssh key that you would give Get hub or get lab orbit bucket or the ssh key that might be on another server already. It's that exact same one. It's your public ssh key, and it even tells you how you can do it here. So there's some nice steps. If you don't have one, you can create one mine's already selected. I don't need to add any tags. I'm gonna choose the host name here. Listen to call that rocket man. I don't like that one that they usually give us. I'm going to enable back ups for a dollar a month. Why not create that droplet? Okay, my droplet is created now. I need to ssh in years. So I need this I p address Right here. 167172113119 times in a copy that Let's open up a terminal and ssh into here. So let's do ssh route at and then the i P address. Yep. Allow me in and hello. We are inside of our server. So now we can run ls be released, Dash a. We can see that we're using you bun to 18.0 4.3. LTs Now, before we do anything, we don't want to be doing anything in the root user. We want to create a new user. It's just a good practices. Little more secure that way. So let's add a new user. I'm gonna call mine Caleb, throw this whole video. I'm going to be calling my user. Caleb, you might want to call yours your name. So whenever you see my name, just feel free to replace it with yours. I'm gonna enter a password here, and I'm gonna do it again. My full name is Caleb Tall in my room number. Work phone, home phone, other. Is that information correct? Yep. Most of that was just empty information. And now we need to make me a super user. So let's do user mod dash a g pseudo Caleb. And while we're here, let's go ahead and make sure that open ssh has enabled through our u f W Now you f w is. What I'm saying is your uncomplicated firewall. And this is the one that comes default with you, Bond Do so let's go ahead and do you FW app list. It's as we've got available Application Your open. Ssh. Let's do you FW allow open. Ssh! And make sure you type in the exact same way was capital O Capital s s and H. You have w enable. Yep. Procedure this operation and you FW status. And this will show us what we have available. So we have now allowed open. Ssh! The reason we're doing this is because that Caleb user that I created is now going to be its own user. So we don't have to Ssh with root weaken. Ssh! With Caleb or with your own user. Now we have one more step before that's actually available. We need to copy over our ssh key from the from the root account to the new account. Let's do our sink, dash, dash, archive dash, dash ch own Caleb Cool. And Caleb did it as this h slash home slash Caleb also, By the way, all these commands I'm going to make available to you in text so you don't have to do it along with me through this whole video. You can watch me do this and then go through the text later. There's a lot of steps and you might not want to pause the video. You know, 100 times that could get pretty annoying. Go ahead, hit. Enter. And now let's just exit this. I'm gonna hit control C or controlled D, and I'm going to ssh, Caleb. And then whatever the I p address was, which happened to be 1671 71 13119 and look at that. I am now in as Caleb and Rocket Man is my server. Now, that's how we create a brand new account or a brand new user rather inside of our new U Bahn to system. I'm going to ssh, Caleb at 167.172 dot 1132119 That I p address is going to be different for you based on the server that you set up. So don't use my i p address because it's not going to let you in. All right. And then one day I got in. So the first thing we need to do is we need to update you bun, too. If you haven't done this already, you're going to want to do this every single time. You start up a new server. So Ryan Sudo, Apt update. It's going to ask for your password, and that's okay. I already have all the stuff updated. You'll probably see a large list of things that have been updated. Might even take a couple minutes, and that's totally okay. The next thing we want to do here is run sudo Apt install python three dash Pip python three Dash Dev Limpy que dev Post rescue Oppose Kresk, You health contraband, genetics and curl. We're going to be using all of these things now again. That might take a little bit of time. When you're doing this for the first time, it's going to say something along the lines of Ah, there's going to be 278 ish megabytes. Do you want to download it? You just click or type. Why and then hit. Enter and it will start downloading for you next. Once you're done that, let's create a new post Greste most post GREss session so soon Oh, Dash, you post grass pea SQL. Now, this point, we need to create a new database called Rocket Man. So you're going to want to run a few different commands you? Actually, what I'm going to do is just move this up a bit. So let's do clear. Get in here and I'm going to walk you through the steps. So step one, you want to create a new database called Rocket Man, so create database rocket man, Don't forget your semi colon at the end. Now what This matches is in our production up high file. We set this already. This is going to match the name. This is your database name. It's called Rocket Man Hit. Enter and that will create a new database for you. Next, we're going to create a new user called Rocket Man. And so this one is create user Rocket man with password and some crazy password in here again. That password matches this one in here in our production dot pie file. So it's create user rocketman a lower case with password, then are crazy password with a semi colon at the end. Next, we need to do a little bit of rule altering here. We're going to alter a role. Rocketman set client encoding to utf eight. And don't worry about typing all this out. I'm gonna put all of this into text format as well, so you can copy and paste most of it just because watching a video and typing it out manually what? And then pausing it, impressing play over and over and over again. Pretty painful. So don't worry about having to do all that you can. You can copy and paste most of this from the learned wait till website. So that one just says said the client encoding to UFT eight the next one we want to alter a role. Rocketman set to default transaction isolation to read committed and that's just setting our default transaction. And next, we want to alter a role. Rocketman set time zone two UTC. So we're just saying set time zone to a regular times when you can totally change these two whatever you want as well. These are the generic settings that I like working with anyway, So my database always thinks that I'm on UTC time. Once all that is done, you are going to need to run one more command here. You're going to need to grant all privileges on the database called Rocket Man. That's this one up here, two of the user called Rocket Man, which is this one right here. Now we just happens to be that the user name and the database are the same. They don't have to be. They can be totally different, but in this case, they're the same, so that might be a little bit confusing, but for the most part, we're pretty much done with that So go ahead, hit. Enter on that. And then when you're done, slash Q. To quit post grass. Next, we need to upgrade and install Pip and Virtue. And so we're going to run pseudo dash H Pip three installed Dash, dash upgrade, Pip And all this is going to do is upgrade pipped to the latest version. Apparently, I've already got the latest version, so I do Pip Dash V and I could do pick three Dash V and we can see is using Python 3.6 Now . I wanted to use Python 3.7, but that's not a problem for this. Python 3.6 is just as well supported as python 3.7 for Wag Tail 2.7. Next, we need to install our virtual end, so we need to do Sudo Dash H Pit three. Install Virtual in and this is just going to install Virgil End now. I already have that installed, so that's not a problem, and you'll see a bunch of taxed on your terminal as well, saying Hey, we're installing it virtually have installed Now. Next, I want to create a new folder in here. So PWD is theory working directory that I'm in. I'm in slash home slash Caleb. If you have a different user, it's going to be different. It's going be slash home slash Steve, If your name is Steve So I'm going to m k dire. Make a directory essentially rocket men. And if you're new to this sort of UNIX like language, this is tilled here is saying home slash that saying home. So home slash Caleb, that's my user and then rocket man And then I can see the into Rocket Man If I do p w d, we can see that home Caleb and Rocket Man is my folder structure. That's where I'm currently in. Now. At this point, we could do ls Dash L. A. And we see absolutely nothing in here. And that's good. We want nothing in there. Don't create a virtual enver anything yet. We want to clone our project in here. First this week gets doesn't complain about trying to clone a project into a folder that already exists, or you having to clone your project into another folder and then recursive lee it, Move all your files up on the command line. That's sort of painful for most people. Instead, what we're going to do is clone our project into this empty directory. So do get clone, and then whatever your repo name is now, this is just a sample repo that I'm using as I record this this whole course. But eventually this is going to be https. Get hub dot com slash quoting for everybody slash rocket man. And then the most important part here is your dot at the end that dot means it's going to install or clone your entire repo into your folder that you're currently in. No, this is going to ask me to log in because I'm using https instead of ssh. You know, if I do ls Dash l. A tadaa. We've got everything in there, including my devi dot escalate three, which probably should not be in there but is in there Anyways, I'm gonna clear that off. Now let's go ahead and create a new virtual end. So let's do virtual and Rocket Man ven now. The reason I called this Rocket Man ven is because a it matches with what we've been working with already with V and V for our local project and be because it matches its already inter get ignore file so we don't have to worry about accidentally committing our whole virtual environment. And now we need to actually get into this. So let's do source Rocket Man vendor bin activate and now we're inside of it. And at this point, if we do pip freeze, that's perfect. You should see absolutely nothing. That means we're inside of a virtual environment that is totally clean. There's nothing preinstalled in there. Now we need to pip install G unicorn organic corn. I'm not really sure how people say that along with psycho PG to dash binary cool beans lets you pick Freeze once more and we'll see we're using G Unicorn version 20.0 point four and cycle PG Binary version 2.8 point four. So clear that off. Make some room here again. Now we want to pip install our requirements file So pip install dash Our requirements dot txt know what that's going to do is we've actually done this before, is going to take our requirements file and install Django Wegel, Django extensions, Jenga widget tweaks and a century sdk. So let's go ahead. Install that all right, All of that is installed, and now we can run our server sort of. Let's let's try to run her server with production settings. So let's do Python managed up. I run server 0.0 dot zero, Court 8000 and let's specify settings. Settings is equal to rocket man dot settings dot production. So this is your Rocket Man folder. This is your settings folder, and this is your production dupuy file. So let's go ahead. Slam that and Ricky and see what happens. We should at this point, see something like this. It says. Missing static file manifest entry for some file. If you see this, this is good news. This means all we have to do is run, collect static. So now we can run Python managed up. I collect static and oh, no, it says we have no module name debug toolbar. And that's because at this point, our servers trying to run Dev instead of running our production settings in here, it's trying to run dev dot txt, and it's looking for installed APS Debo Tuller that doesn't exist that only exist locally because we were doing pip install Dash are Dev dot txt, which comes with Django debug toolbar, PDB and everything else in the Requirements file. We didn't do that for the server, and we don't want to do that for the server. We don't want Django Debug Tool are installed and we do not want dev dot pie to be used. We want our production settings to be used, so let's go ahead. Collect static and let's do settings is equal to Rocket man dot setting Stott production. And we'll fix this up in just a little bit so that we don't have to specify settings all the time. And what Ah, 279 static files copied 357 post process and it all went into our static directory in home . Caleb Rocket man slash static. Now, if we do to do to do, if we run our server again, we should see that you have 109 un applied migrations or however many you have for your project. Your project might be different from my project. I believe at this point time wait till comes with 89 un applied migrations. So we simply added 20 more migrations. That's totally okay. I'm gonna Council that because that's not going to do anything for us at this point in time . What we need to dio is we need Teoh first. We'll get rid of that settings flag because that's super annoying. You could live your life that way, but I don't want to. That settings flag is the settings is equal to Rocket man dot settings dot production like that, Yeah, no, that's that's annoying to right every single time. So we're going to get rid of that. And instead, what we're going to do is export Django settings. Module is equal to, and this is a string rocket man DOT settings don't production. And now, if we echo Django Settings module, we can see that it returns. Rocketman settings up production. Now if I run jangle run server just like this, we're going to see it's automatically using Rocket Man settings dot production. It wasn't doing this before, and if we collect static, we don't need to do collect static with the settings flag, either. So let's to do to do let's just as an example, collect static, but let's get rid of these settings. Flags. Yep, and it worked perfectly fine, but Now, when we run our server, we can see we have 109 un applied migrations. Let's let's go ahead and migrate those. So cancel your server with control. See, that's to python manage dot pie Migrate on and it's just gonna migrate All 109 migrations or however many migrations you have now that looks good. Let's go ahead and create a new super user with python managed up high, Create super user and my name is going to be Caleb. My email address is Caleb at learned wag tail dot com Password is I'm not gonna say it aloud and I have a super user now. A little while ago, we checked u f w who said pseudo you fw status, and we made sure that open ssh was available to us. Let's go ahead and open up Port 8000 sets do pseudo you FW allow 8000 and this is going to allow us to view our website on our servers I p address with a port in the URL and C status , and we should see this. Now if we open up our browser here and go to http and then whatever your port is are not your port your i p address. So my my i p addresses 167172113119 Port 8000. That 8000 is important. That 8000 is going to come from python managed up high run server Port 8000 at the end there. So let's run that and refresher page and we can see we've got an internal server error. Now let's go back to our century when we installed century and it says that we have our first event. That's perfect. Now, if you don't have an error, you can totally skip this part. But if you do have an error, century is going to help you out immensely. So take me to my event, and this one that we're looking at here is actually an older one. That was when we initially ran, collect static, and we saw that static files manifest air. That's what this one is. So that's cool. That got logged. We've got another value. Aaron. Here. Missing static files manifest injury for images. Contact overlay. Now, this is where debugging can get kind of painful, so usually what I do is I just grab this and I'll search through my whole project for it. So let's go into visual studio code or whichever editor you like best. And I'm just going to do a global search for images. Contact overlay dot PNG And here I see static. Well, this is starting to look promising because static manifest static files manifest. Can't find this file. What does it exist? Let's go ahead and take a look. Templates. We want static images. Contact overlay. Sure enough, that does actually exist. So what's the deal here? Chances are it's probably just that slash so we could save us, commit and do it get pull if we wanted to. Or if you want to do a little cowboy coding, we can stop our server and we could do pseudo nano. Or, if you're more of a wizard, you can use vim as well. I'm going to use Nano because it's a little easier for people who are brand new to this this sort of thing. So pseudo nano Rocket man slash templates slash includes slash footer dot html and that matches Rocket Man templates includes footer dot html, and I can go in here and do to do to move all the way over, and I just want to get rid of this now to save, I hit control. Oh, and exit A hit control X. Now let's run our server one more time and let's see what happens. Let's see what happens when we refresh here. Look at that. Our site is loading now. It doesn't look like this is much, but it will be quite a bit in just a little bit. So in here, we can say that one's been resolved. Let's go back in here. We know that this one's been resolved. That was our 1st 1 And, ah, this is when it hit the page for a second time. It's missing contact overly, so I know that one's been resolved as well. Girl, we currently have zero issues. Now this looks ugly, but it is actually functional. So if we go to the ad men, this is not going to look very nice. But I can put in my information here my user name and password, and I consigned in. Now again, none of this is going to load nicely, will fix us up in just a little bit, but this is proof that our CMS is up and running and you are actually running python code on your server. Let's go back to our terminal and hit, cancel or control, See to cancel. And let's just make sure that we're all in the same directory here. So I'm in home. Caleb Rocket Man. If you're not here, you can do CD tilled Home Slash Rocket Man, and that will bring you exactly where I am. Now let's try running this without the Python run Server Command. Let's try running this with G Unicorns, Atsugi, Unicorn Dash, Dash, Bind and we're going to bind local host Port 8000 two Rocket man dot Whiskey and let's go back to our site here and just hit Refresh. Check it out. It's still running. It's still ugly, but it's still running now. This time it's running with the unicorn, and it's not running with jangle run server. This is good news. Now let's go ahead and make this actually work nicely. So, first of all, we don't want to have to run to Unicorn Bind 0.0 dot 0.0 Port 8000 Rocket Mandel Whiskey and have that running all the time. We just wanted to run automatically in the background. So it's deactivate out of our virtual environment. And let's pseudo nano at sea system d slash system slash g unicorn dot sock in. We're going to create a socket file here and in here, and I just pasted that ends. You don't have to watch me type it. Ah, And here we have units, which is our descriptions. Unicorn socket. We've got a socket telling it which socket filed to use and a bunch of installs headings. So let's go ahead, Control. Oh, control X. Once you've copied that into your at sea system D system G unicorn dot socket, That socket file is very important. This will not run without a socket. Next, we want a pseudo nano or them if you'd rather use them or any other command line editor Pseudo nano at sea system D system to unicorn dot service Now these names need to match. Si Chef, the unicorn dot socket n g unicorn dot service Now in here, we actually have quite a bit more. So we've got unit again. We've got that description requires g unicorn socket. Remember, that's why the name matches and inter service. This is actually the most important part we have. Our user is equal to Caleb. So it's going to be whatever your user name is if used a different user name. Group is dub, Dub, Dub dash data. That's okay. Are working Directory is home slash Caleb slash rocketman. Again, this is going to be different. If you have a different user name and a different project name, yours might say home slash steve slash Whatever your project name is exact start is again. This is going to be a path thing. So home slash your user name slash your project name slash your virtual end of name slash bin slash g unicorn. And then also, you need to make sure that down here where it says rocket mandate Whiskey is your project name dot whiskey. Very, very important. If you run into the unicorn problems in the future in just a few more steps, chances are this was set incorrectly and you have a path thing issue. Now, if we scroll down, there's nothing really else to talk about in there again, we just need to make sure that those path are absolutely correct. Let's go ahead. Control Ode to Save Control X to exit. Let's start the unicorn socket. We're gonna do that with pseudo system CTL start G Unicorn got socket And then let's enable this socket as well. With pseudo system CTL enabled the unicorn dot socket and it created a sibling just good. And now, at this point, we could do system CTL status g unicorn dot socket and get the status of this socket and you should see active listening. It might not be in green. It might be in white. That's OK. It's just at this point should say active listening. Next, we need to check for the existence of this socket, so we just want to make sure that the socket does exist. So let's do file slash run g unicorn dot socket and that's actually wrong. It's not suck. There we go. And so this should just output that it's a socket file. Now let's go ahead and check the status of the unicorn entirely so pseudo system CTL status to unicorn. And at this point, don't be alarmed, but it should say inactive dead. Now we contest thes socket activation with a simple Curl command. This is why we installed Curl much, much early on so we could do Curled dash dash UNIX dash socket slash run slash g unicorn dot sock local host. Now, when we hit enter, we should be seeing the output of our site all in html. But we should see the output of it just like that. We've got oh sorts of stuff in here, and this is the output of our home dot html or home page dot html template. Now, if you don't see this, if you see a 400 or if you see some other error first of all, check century make sure that there are no errors in there. If there is an error, make sure you solve that error first. If there are no errors in century, chances are the problem is with either the G unicorn out socket file or the G unicorn. That service file, you're gonna want to go back a couple of steps and just touch those up. Probably it's just an issue with path thing. The other thing to check to is in your project. If you're running into an issue, just make sure that this is rocket man dot setting stop production. So I'm gonna clear this off. And at this point, it doesn't really hurt to reload our system, Damon. So let's do pseudo system CTL demon reload and pseudo system restart. Gee, unicorn. Now let's go ahead and set up engine X. This is really, really important because Engine X is going to be pretty much our proxy to and from G Unicorn. We're going to need this to also load all of our static files so that it doesn't have to go through python because for someone to hit your website and then have that request go through python toe, look up a file and then serve that file is a lot slower than just letting a service that's solely dedicated to that. Like Engine X. Do that work. So let's do pseudo nano at sea engine X sites dash available slash rocketman. This file should not exist at this point, and I just pasted a config in here. But we are going to go over this so it's going to look Listen for port 80. That's your standard, Http or your website port goingto listen again. Report 80 And here the server name is my server I p address. I'm also going to put in here my final I P address or my final domain, not I p address. So, rocket Man, don't learn wayto dot com. I'm gonna put an error log in here, so it's going to log directly into my project as engine X dash air dot log I said the location of the fabric on I said the static assets. So again, this comes down to passing. So I'm always in the home directory. I'm using the user Caleb and my project. My file in my folder that's holding my project is called Rock It, Man. And when we did collect static, it created folder for us called Static. And so this is really just creating an alias. So when you go to your website dot com slash static slash image dot PNG is actually saying , Hey, that imaged a PNG doesn't just live in a static folder. It lives in this exact static folder. And your media is where all of your media is going to be uploaded with a wag tail project. And then whenever you hit any page, your home page really here, eyes going to include some proxy parameters and is going to pass that G unicorn dot sock filed That file that we tested to make sure exists were saying, Hey, pass it through there. Let's go ahead, Hit control Oh, and control X. And again, if I'm going a little too fast with some of this stuff, you can always reference this code on learned wag tail dot com. So now we need to create a sibling here, and so we can do that with pseudo l N Dash s. And then this just comes from the rocketman file. That's the one that we added it up here we're going to similar gets to etc. Engine X sites enabled. So basically, we're just saying, Hey, the site is now enabled. We created a config before and now we're saying that config is enabled. Let's go ahead and test engine X with pseudo engine X Dashti Things were looking okay and let's restart engine X at this point system CTO restart engine X. So far, no problems. We can even dio status and Yonex, we can see that it's running now. This point, if you view your website, chances are it's not going to run. And sure enough, I just refresh the page. And yet it's not running yet. That's okay. We have a couple more steps to do here. First things first. We need to delete our port 8000 from you, FW. So if we do pseudo you FW status, we're gonna see Port 8000 and there's let's go ahead and get rid of that because we don't need that. So sudo you FW delete, allow 8000 and it's the status again. It's no longer in there Now. We need to allow Engine X in here, though, so pseudo you FW allow engine X full and noticed the apostrophes around this. And if we do status, you should see something that looks like this. Now, let's go back to our page. And I'm just gonna get rid of this port 8000 cause we no longer need that and check it out . We have static assets. Everything looks OK. We can go back to our home page and I'm just on my i P address Now. There's no content in here, so there's no images or text or anything. But hey, this is working. And if I go to slash add men. Everything is working in here. And let's just test the debug is off by going to like 40404044 page not found or this page cannot be found. If you see the regular yellow page that you see, Ali template does not exist or page does not exist or any sort of error that you usually see with local Django development. That means debug is still on and you're probably not using your production settings. And that means Django is probably serving a whole fear static assets, which is slow and insecure. You do not want that. But for us we have everything up and running the way we wanted Teoh. Now I were next step is to get rid of this I p address. We don't want that I p address in there at all. This isn't 1985 anymore. We can actually have a domain name. So, uh, doesn't really matter who your domain is registered with. Mine is registered with Google and so it's just the learned waittil website. And if I go into DNS and again, if you're with Go Daddy or Cloudflare anything like that, you have access to your DNS and down here in my custom resource records, I just want to put Rocket Man time. Let's give it 10 minutes or if you're using go Daddy, I think they do seconds. It would be like 600 seconds for 10 minutes. And I put my I p address in there. This is my server. I p address 167172113119 Click add. And usually it will say something like, they'll take effect within the next 48 hours. But typically, registrars AARP it pretty fast Go. Daddy is kind of slow, but typically, this happens fairly quickly. Next, we're going to want to make sure that this is allowed in our engine X file. So where is it that did it? A pseudo nano, etc. Engine X sites available. Rocket Man, Let's go ahead and get rid of this I p address in here. We no longer need that because I am using Rocket Man. Don't learn wayto dot com now, the DNS might not have kicked in yet, so I might have to wait a little bit, but I know eventually it's going to work next, I need to change one more thing. Pseudo nano rocket man slash settings, slash production And I want to get rid of this asterisk in here. If you wanted to be more secure, you could put your i P address in there. But it's not really necessary because it's going to be accepting local host, which is Engine X and also your domain. So mine is rocketman dot learn wag tail dot com. Yours might just be super awesome. Wag tail website dot com Go ahead, say that. And now because we made a change to our rocket man And you next, things we have to do and her next Dashti that would just make sure that we don't have any typos or anything in there. Pseudo system CTL restart Engine X and pseudo system restart G unicorn. Now, if we would over server again And in rul we have the i p address. This should no longer work. Just brings you to the engine x page, which is exactly what you should see. And if we go two Rocket man, don't learn wag tail dot com For whatever reason, this wants me to use https. We don't have https installed. However, if you do want to add SSL, definitely look into certain pot. Now. For me, this is simply a cashing thing to my browser saying Hey, you've hit the site before. It should have https on it, but it currently doesn't. So I just opened up new incognita window or private browsing mode and put in Rocket Man Don't learn. Wait tell dot com Let's go to my head, men. We're so close Monologue in And now I can change everything I want. Now one last thing we should probably do is go into your wag tail settings Goingto sites change that from local host and put in your domain. So I'm just going to put in Rocket Man. Don't learn wag tail dot com Once you do at an SSL, and I'm going to leave that up to you. But once you do add an SSL, you're going to want changes to port 443 That is your secure port for ssl slash TLS encryption. That gives you that little lock up here. We don't have that were just on port 80 and host name is Rocket Man. Don't learn. Wait till dot com and this is called Rocket Man. And at this point, your website is launched. Now, you just need to add your content. I'll add content to this so that you can see a fully working demo of the site. But for now, your site is up and running. Now, if you're thinking holy smokes, Caleb, that is Well, that was a lot of work. There are a lot of steps. Yes, you're right. There is a lot of steps that's usually goes into launching a website is quite a few steps. This is why Dev Ops is a pretty big thing. Oh, and one more thing. Just while we have it in here. If I do get status, I'm gonna want to add this to my get ignore. And I'm going to want to commit these files to the Rebo as well. I'm gonna do that behind the scenes, and I'm going to let you take care of that as well. Remember, if you have any issues, you can always check your Internets error log. You can always check century. I always check century first just because most of my heirs tend to come from python or some sort of thing that's happening in jangle or wag tail and it has less to do with Andrew necks and to unicorn. Once those were actually set up and back to what I was saying about all these different steps. Yes, there are a lot of different steps. I actually have these written out. There are roughly 55 or 60 steps somewhere in there. I'm going to again put these all into writing for you so you can have step one Step two Step three and you can basically copy and paste with some slight modifications in there. This way you can launch a website in less than an hour. You can actually just copy and paste and have the website up and running in on digital ocean. At least I got this up and running in about 11 minutes. So next time you launch a website, it'll be a lot faster. And there we go. We have completed the Rocketman website. I hope you've enjoyed this course. I hope you've learned a lot. But most of all I hope to see you in the Wag Tail Slack channel. If you go to wait, tell dot io slash slack. You can get an invites to our slack channel. Where there's, I know, 1000 or more weight deal. Debs, come join us in the support channel. You can always ask questions there and more importantly, you can help other people who might have run into problems that you have recently run into as well. And I hope to see you and your websites in the future.