diff --git a/en/css/README.md b/en/css/README.md index d7736822530..7b8e1cd31da 100644 --- a/en/css/README.md +++ b/en/css/README.md @@ -68,7 +68,7 @@ djangogirls Time to write some CSS! Open up the `blog/static/css/blog.css` file in your code editor. -We won't be going too deep into customizing and learning about CSS here. There is a recommendation for a free CSS course at the end of this page if you would like to learn more. +We won't be going too deep into customizing and learning about CSS here. There is a recommendation for a free CSS course at the end of this page if you would like to learn more. But let's do at least a little. Maybe we could change the color of our headers? To understand colors, computers use special codes. These codes start with `#` followed by 6 letters (A–F) and numbers (0–9). For example, the code for blue is `#0000FF`. You can find the color codes for many colors here: http://www.colorpicker.com/. You may also use [predefined colors](http://www.w3schools.com/colors/colors_names.asp), such as `red` and `green`. @@ -128,11 +128,11 @@ Your file should now look like this:

Django Girls Blog

- {% for post in posts %} + {% for blogpost in blogposts %}
-

published: {{ post.published_date }}

-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

published: {{ blogpost.published_date }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} @@ -180,7 +180,7 @@ h1 a, h2 a { Great! -As mentioned above, CSS has a concept of classes. These allow you to name a part of the HTML code and apply styles only to this part, without affecting other parts. This can be super helpful! Maybe you have two divs that are doing something different (like your header and your post). A class can help you make them look different. +As mentioned above, CSS has a concept of classes. These allow you to name a part of the HTML code and apply styles only to this part, without affecting other parts. This can be super helpful! Maybe you have two divs that are doing something different (like your header and your blog post). A class can help you make them look different. Go ahead and name some parts of the HTML code. Add a class called `page-header` to your `div` that contains your header, like this: @@ -191,14 +191,14 @@ Go ahead and name some parts of the HTML code. Add a class called `page-header` ``` -And now add a class `post` to your `div` containing a blog post. +And now add a class `blogpost` to your `div` containing a blog post. {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html
-

published: {{ post.published_date }}

-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

published: {{ blogpost.published_date }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

``` @@ -254,15 +254,15 @@ h1, h2, h3, h4 { } ``` -Then surround the HTML code which displays the posts with declarations of classes. Replace this: +Then surround the HTML code which displays the blog posts with declarations of classes. Replace this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html -{% for post in posts %} +{% for blogpost in blogposts %}
-

published: {{ post.published_date }}

-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

published: {{ blogpost.published_date }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} ``` @@ -274,13 +274,13 @@ in the `blog/templates/blog/post_list.html` with this:
- {% for post in posts %} + {% for blogpost in blogposts %}
-

published: {{ post.published_date }}

+

published: {{ blogpost.published_date }}

-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %}
@@ -300,4 +300,3 @@ Don't be afraid to tinker with this CSS a little bit and try to change some thin We really recommend taking the free online courses "Basic HTML & HTML5" and "Basic CSS" on [freeCodeCamp](https://learn.freecodecamp.org/). They can help you learn all about making your websites prettier with HTML and CSS. Ready for the next chapter?! :) - diff --git a/en/deploy/README.md b/en/deploy/README.md index 3ecba30b47b..3c8eafd7bd0 100644 --- a/en/deploy/README.md +++ b/en/deploy/README.md @@ -77,7 +77,7 @@ Linux and MacOS treat files with a name that starts with `.` (such as `.gitignor and the normal `ls` command won't show these files. Instead use `ls -a` to see the `.gitignore` file. -> **Note** One of the files you specified in your `.gitignore` file is `db.sqlite3`. That file is your local database, where all of your users and posts are stored. We'll follow standard web programming practice, meaning that we'll use separate databases for your local testing site and your live website on PythonAnywhere. The PythonAnywhere database could be SQLite, like your development machine, but usually you will use one called MySQL which can deal with a lot more site visitors than SQLite. Either way, by ignoring your SQLite database for the GitHub copy, it means that all of the posts and superuser you created so far are going to only be available locally, and you'll have to create new ones on production. You should think of your local database as a good playground where you can test different things and not be afraid that you're going to delete your real posts from your blog. +> **Note** One of the files you specified in your `.gitignore` file is `db.sqlite3`. That file is your local database, where all of your users and blog posts are stored. We'll follow standard web programming practice, meaning that we'll use separate databases for your local testing site and your live website on PythonAnywhere. The PythonAnywhere database could be SQLite, like your development machine, but usually you will use one called MySQL which can deal with a lot more site visitors than SQLite. Either way, by ignoring your SQLite database for the GitHub copy, it means that all of the blog posts and superuser you created so far are going to only be available locally, and you'll have to create new ones on production. You should think of your local database as a good playground where you can test different things and not be afraid that you're going to delete your real posts from your blog. It's a good idea to use a `git status` command before `git add` or whenever you find yourself unsure of what has changed. This will help prevent any surprises from happening, such as wrong files being added or committed. The `git status` command returns information about any untracked/modified/staged files, the branch status, and much more. The output should be similar to the following: @@ -199,7 +199,7 @@ As you watch that running, you'll be able to see what it's doing: On PythonAnywhere all those steps are automated, but they're the same steps you would have to go through with any other server provider. -The main thing to notice right now is that your database on PythonAnywhere is actually totally separate from your database on your own computer, so it can have different posts and admin accounts. As a result, just as we did on your own computer, we need to initialize the admin account with `createsuperuser`. PythonAnywhere has automatically activated your virtualenv for you, so all you need to do is run: +The main thing to notice right now is that your database on PythonAnywhere is actually totally separate from your database on your own computer, so it can have different blog posts and admin accounts. As a result, just as we did on your own computer, we need to initialize the admin account with `createsuperuser`. PythonAnywhere has automatically activated your virtualenv for you, so all you need to do is run: {% filename %}PythonAnywhere command-line{% endfilename %} ``` @@ -250,9 +250,9 @@ And remember, your coach is here to help! # Check out your site! -The default page for your site should say "It worked!", just like it does on your local computer. Try adding `/admin/` to the end of the URL, and you'll be taken to the admin site. Log in with the username and password, and you'll see you can add new Posts on the server -- remember, the posts from your local test database were not sent to your live blog. +The default page for your site should say "It worked!", just like it does on your local computer. Try adding `/admin/` to the end of the URL, and you'll be taken to the admin site. Log in with the username and password, and you'll see you can add new BlogPosts on the server -- remember, the blog posts from your local test database were not sent to your live blog. -Once you have a few posts created, you can go back to your local setup (not PythonAnywhere). From here you should work on your local setup to make changes. This is a common workflow in web development – make changes locally, push those changes to GitHub, and pull your changes down to your live Web server. This allows you to work and experiment without breaking your live Web site. Pretty cool, huh? +Once you have a few blog posts created, you can go back to your local setup (not PythonAnywhere). From here you should work on your local setup to make changes. This is a common workflow in web development – make changes locally, push those changes to GitHub, and pull your changes down to your live Web server. This allows you to work and experiment without breaking your live Web site. Pretty cool, huh? Give yourself a *HUGE* pat on the back! Server deployments are one of the trickiest parts of web development and it often takes people several days before they get them working. But you've got your site live, on the real Internet! diff --git a/en/django_admin/README.md b/en/django_admin/README.md index c6ae54059d9..d0fba94b3dd 100644 --- a/en/django_admin/README.md +++ b/en/django_admin/README.md @@ -7,14 +7,14 @@ Let's open the `blog/admin.py` file in the code editor and replace its contents {% filename %}blog/admin.py{% endfilename %} ```python from django.contrib import admin -from .models import Post +from .models import BlogPost -admin.site.register(Post) +admin.site.register(BlogPost) ``` -As you can see, we import (include) the Post model defined in the previous chapter. To make our model visible on the admin page, we need to register the model with `admin.site.register(Post)`. +As you can see, we import (include) the BlogPost model defined in the previous chapter. To make our model visible on the admin page, we need to register the model with `admin.site.register(BlogPost)`. -OK, time to look at our Post model. Remember to run `python manage.py runserver` in the console to run the web server. Go to your browser and type the address http://127.0.0.1:8000/admin/. You will see a login page like this: +OK, time to look at our BlogPost model. Remember to run `python manage.py runserver` in the console to run the web server. Go to your browser and type the address http://127.0.0.1:8000/admin/. You will see a login page like this: ![Login page](images/login_page2.png) @@ -46,7 +46,7 @@ Return to your browser. Log in with the superuser's credentials you chose; you s ![Django admin](images/django_admin3.png) -Go to Posts and experiment a little bit with it. Add five or six blog posts. Don't worry about the content –- it's only visible to you on your local computer -- you can copy-paste some text from this tutorial to save time. :) +Go to BlogPosts and experiment a little bit with it. Add five or six blog posts. Don't worry about the content –- it's only visible to you on your local computer -- you can copy-paste some text from this tutorial to save time. :) Make sure that at least two or three posts (but not all) have the publish date set. It will be helpful later. diff --git a/en/django_forms/README.md b/en/django_forms/README.md index 71a00725ce8..f833b0a7020 100644 --- a/en/django_forms/README.md +++ b/en/django_forms/README.md @@ -4,7 +4,7 @@ The final thing we want to do on our website is create a nice way to add and edi The nice thing about Django forms is that we can either define one from scratch or create a `ModelForm` which will save the result of the form to the model. -This is exactly what we want to do: we will create a form for our `Post` model. +This is exactly what we want to do: we will create a form for our `BlogPost` model. Like every important part of Django, forms have their own file: `forms.py`. @@ -21,22 +21,22 @@ OK, let's open it in the code editor and type the following code: ```python from django import forms -from .models import Post +from .models import BlogPost -class PostForm(forms.ModelForm): +class BlogPostForm(forms.ModelForm): class Meta: - model = Post + model = BlogPost fields = ('title', 'text',) ``` -We need to import Django forms first (`from django import forms`) and our `Post` model (`from .models import Post`). +We need to import Django forms first (`from django import forms`) and our `BlogPost` model (`from .models import BlogPost`). -`PostForm`, as you probably suspect, is the name of our form. We need to tell Django that this form is a `ModelForm` (so Django will do some magic for us) – `forms.ModelForm` is responsible for that. +`BlogPostForm`, as you probably suspect, is the name of our form. We need to tell Django that this form is a `ModelForm` (so Django will do some magic for us) – `forms.ModelForm` is responsible for that. -Next, we have `class Meta`, where we tell Django which model should be used to create this form (`model = Post`). +Next, we have `class Meta`, where we tell Django which model should be used to create this form (`model = BlogPost`). -Finally, we can say which field(s) should end up in our form. In this scenario we want only `title` and `text` to be exposed – `author` should be the person who is currently logged in (you!) and `created_date` should be automatically set when we create a post (i.e. in the code), right? +Finally, we can say which field(s) should end up in our form. In this scenario we want only `title` and `text` to be exposed – `author` should be the person who is currently logged in (you!) and `created_date` should be automatically set when we create a blog post (i.e. in the code), right? And that's it! All we need to do now is use the form in a *view* and display it in a template. @@ -98,7 +98,7 @@ And the final code will look like this: {% filename %}blog/urls.py{% endfilename %} ```python -from django.urls import path +from django.urls import path from . import views urlpatterns = [ @@ -116,7 +116,7 @@ Time to open the `blog/views.py` file in the code editor and add the following l {% filename %}blog/views.py{% endfilename %} ```python -from .forms import PostForm +from .forms import BlogPostForm ``` And then our *view*: @@ -124,11 +124,11 @@ And then our *view*: {% filename %}blog/views.py{% endfilename %} ```python def post_new(request): - form = PostForm() + form = BlogPostForm() return render(request, 'blog/post_edit.html', {'form': form}) ``` -To create a new `Post` form, we need to call `PostForm()` and pass it to the template. We will go back to this *view*, but for now, let's quickly create a template for the form. +To create a new `BlogPost` form, we need to call `BlogPostForm()` and pass it to the template. We will go back to this *view*, but for now, let's quickly create a template for the form. ## Template @@ -162,7 +162,7 @@ Time to refresh! Yay! Your form is displayed! But, wait a minute! When you type something in the `title` and `text` fields and try to save it, what will happen? -Nothing! We are once again on the same page and our text is gone… and no new post is added. So what went wrong? +Nothing! We are once again on the same page and our text is gone… and no new blog post is added. So what went wrong? The answer is: nothing. We need to do a little bit more work in our *view*. @@ -173,7 +173,7 @@ Open `blog/views.py` once again in the code editor. Currently all we have in the {% filename %}blog/views.py{% endfilename %} ```python def post_new(request): - form = PostForm() + form = BlogPostForm() return render(request, 'blog/post_edit.html', {'form': form}) ``` @@ -186,14 +186,14 @@ So in our *view* we have two separate situations to handle: first, when we acces if request.method == "POST": [...] else: - form = PostForm() + form = BlogPostForm() ``` -It's time to fill in the dots `[...]`. If `method` is `POST` then we want to construct the `PostForm` with data from the form, right? We will do that as follows: +It's time to fill in the dots `[...]`. If `method` is `POST` then we want to construct the `BlogPostForm` with data from the form, right? We will do that as follows: {% filename %}blog/views.py{% endfilename %} ```python -form = PostForm(request.POST) +form = BlogPostForm(request.POST) ``` The next thing is to check if the form is correct (all required fields are set and no incorrect values have been submitted). We do that with `form.is_valid()`. @@ -203,13 +203,13 @@ We check if the form is valid and if so, we can save it! {% filename %}blog/views.py{% endfilename %} ```python if form.is_valid(): - post = form.save(commit=False) - post.author = request.user - post.published_date = timezone.now() - post.save() + blogpost = form.save(commit=False) + blogpost.author = request.user + blogpost.published_date = timezone.now() + blogpost.save() ``` -Basically, we have two things here: we save the form with `form.save` and we add an author (since there was no `author` field in the `PostForm` and this field is required). `commit=False` means that we don't want to save the `Post` model yet – we want to add the author first. Most of the time you will use `form.save()` without `commit=False`, but in this case, we need to supply it. `post.save()` will preserve changes (adding the author) and a new blog post is created! +Basically, we have two things here: we save the form with `form.save` and we add an author (since there was no `author` field in the `BlogPostForm` and this field is required). `commit=False` means that we don't want to save the `BlogPost` model yet – we want to add the author first. Most of the time you will use `form.save()` without `commit=False`, but in this case, we need to supply it. `blogpost.save()` will preserve changes (adding the author) and a new blog post is created! Finally, it would be awesome if we could immediately go to the `post_detail` page for our newly created blog post, right? To do that we need one more import: @@ -218,14 +218,14 @@ Finally, it would be awesome if we could immediately go to the `post_detail` pag from django.shortcuts import redirect ``` -Add it at the very beginning of your file. And now we can say, "go to the `post_detail` page for the newly created post": +Add it at the very beginning of your file. And now we can say, "go to the `post_detail` page for the newly created blog post": {% filename %}blog/views.py{% endfilename %} ```python -return redirect('post_detail', pk=post.pk) +return redirect('post_detail', pk=blogpost.pk) ``` -`post_detail` is the name of the view we want to go to. Remember that this *view* requires a `pk` variable? To pass it to the views, we use `pk=post.pk`, where `post` is the newly created blog post! +`post_detail` is the name of the view we want to go to. Remember that this *view* requires a `pk` variable? To pass it to the views, we use `pk=blogpost.pk`, where `blogpost` is the newly created blog post! OK, we've talked a lot, but we probably want to see what the whole *view* looks like now, right? @@ -233,32 +233,32 @@ OK, we've talked a lot, but we probably want to see what the whole *view* looks ```python def post_new(request): if request.method == "POST": - form = PostForm(request.POST) + form = BlogPostForm(request.POST) if form.is_valid(): - post = form.save(commit=False) - post.author = request.user - post.published_date = timezone.now() - post.save() - return redirect('post_detail', pk=post.pk) + blogpost = form.save(commit=False) + blogpost.author = request.user + blogpost.published_date = timezone.now() + blogpost.save() + return redirect('post_detail', pk=blogpost.pk) else: - form = PostForm() + form = BlogPostForm() return render(request, 'blog/post_edit.html', {'form': form}) ``` Let's see if it works. Go to the page http://127.0.0.1:8000/post/new/, add a `title` and `text`, save it… and voilà! The new blog post is added and we are redirected to the `post_detail` page! -You might have noticed that we are setting the publish date before saving the post. Later on, we will introduce a _publish button_ in __Django Girls Tutorial: Extensions__. +You might have noticed that we are setting the publish date before saving the blogpost. Later on, we will introduce a _publish button_ in __Django Girls Tutorial: Extensions__. That is awesome! -> As we have recently used the Django admin interface, the system currently thinks we are still logged in. There are a few situations that could lead to us being logged out (closing the browser, restarting the DB, etc.). If, when creating a post, you find that you are getting errors referring to the lack of a logged-in user, head to the admin page http://127.0.0.1:8000/admin and log in again. This will fix the issue temporarily. There is a permanent fix awaiting you in the __Homework: add security to your website!__ chapter after the main tutorial. +> As we have recently used the Django admin interface, the system currently thinks we are still logged in. There are a few situations that could lead to us being logged out (closing the browser, restarting the DB, etc.). If, when creating a blog post, you find that you are getting errors referring to the lack of a logged-in user, head to the admin page http://127.0.0.1:8000/admin and log in again. This will fix the issue temporarily. There is a permanent fix awaiting you in the __Homework: add security to your website!__ chapter after the main tutorial. ![Logged in error](images/post_create_error.png) ## Form validation -Now, we will show you how cool Django forms are. A blog post needs to have `title` and `text` fields. In our `Post` model we did not say that these fields (as opposed to `published_date`) are not required, so Django, by default, expects them to be set. +Now, we will show you how cool Django forms are. A blog post needs to have `title` and `text` fields. In our `BlogPost` model we did not say that these fields (as opposed to `published_date`) are not required, so Django, by default, expects them to be set. Try to save the form without `title` and `text`. Guess what will happen! @@ -269,13 +269,13 @@ Django is taking care to validate that all the fields in our form are correct. I ## Edit form -Now we know how to add a new post. But what if we want to edit an existing one? This is very similar to what we just did. Let's create some important things quickly. (If you don't understand something, you should ask your coach or look at the previous chapters, since we covered all these steps already.) +Now we know how to add a new blogpost. But what if we want to edit an existing one? This is very similar to what we just did. Let's create some important things quickly. (If you don't understand something, you should ask your coach or look at the previous chapters, since we covered all these steps already.) Open `blog/templates/blog/post_detail.html` in the code editor and add the line {% filename %}blog/templates/blog/post_detail.html{% endfilename %} ```html - + ``` so that the template will look like this: @@ -286,14 +286,14 @@ so that the template will look like this: {% block content %}
- {% if post.published_date %} + {% if blogpost.published_date %}
- {{ post.published_date }} + {{ blogpost.published_date }}
{% endif %} - -

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+ +

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endblock %} ``` @@ -312,32 +312,32 @@ Let's open `blog/views.py` in the code editor and add this at the very end of th {% filename %}blog/views.py{% endfilename %} ```python def post_edit(request, pk): - post = get_object_or_404(Post, pk=pk) + blogpost = get_object_or_404(BlogPost, pk=pk) if request.method == "POST": - form = PostForm(request.POST, instance=post) + form = BlogPostForm(request.POST, instance=blogpost) if form.is_valid(): - post = form.save(commit=False) - post.author = request.user - post.published_date = timezone.now() - post.save() - return redirect('post_detail', pk=post.pk) + blogpost = form.save(commit=False) + blogpost.author = request.user + blogpost.published_date = timezone.now() + blogpost.save() + return redirect('post_detail', pk=blogpost.pk) else: - form = PostForm(instance=post) + form = BlogPostForm(instance=blogpost) return render(request, 'blog/post_edit.html', {'form': form}) ``` -This looks almost exactly the same as our `post_new` view, right? But not entirely. For one, we pass an extra `pk` parameter from `urls`. Next, we get the `Post` model we want to edit with `get_object_or_404(Post, pk=pk)` and then, when we create a form, we pass this post as an `instance`, both when we save the form… +This looks almost exactly the same as our `post_new` view, right? But not entirely. For one, we pass an extra `pk` parameter from `urls`. Next, we get the `BlogPost` model we want to edit with `get_object_or_404(BlogPost, pk=pk)` and then, when we create a form, we pass this blog post as an `instance`, both when we save the form… {% filename %}blog/views.py{% endfilename %} ```python -form = PostForm(request.POST, instance=post) +form = BlogPostForm(request.POST, instance=blogpost) ``` -…and when we've just opened a form with this post to edit: +…and when we've just opened a form with this blog post to edit: {% filename %}blog/views.py{% endfilename %} ```python -form = PostForm(instance=post) +form = BlogPostForm(instance=blogpost) ``` OK, let's test if it works! Let's go to the `post_detail` page. There should be an edit button in the top-right corner: @@ -356,7 +356,7 @@ If you need more information about Django forms, you should read the documentati ## Security -Being able to create new posts by clicking a link is awesome! But right now, anyone who visits your site will be able to make a new blog post, and that's probably not something you want. Let's make it so the button shows up for you but not for anyone else. +Being able to create new blog posts by clicking a link is awesome! But right now, anyone who visits your site will be able to make a new blog post, and that's probably not something you want. Let's make it so the button shows up for you but not for anyone else. Open `blog/templates/blog/base.html` in the code editor, find our `page-header` `div` and the anchor tag you put in there earlier. It should look like this: @@ -374,15 +374,15 @@ We're going to add another `{% if %}` tag to this, which will make the link show {% endif %} ``` -This `{% if %}` will cause the link to be sent to the browser only if the user requesting the page is logged in. This doesn't protect the creation of new posts completely, but it's a good first step. We'll cover more security in the extension lessons. +This `{% if %}` will cause the link to be sent to the browser only if the user requesting the page is logged in. This doesn't protect the creation of new blog posts completely, but it's a good first step. We'll cover more security in the extension lessons. -Remember the edit icon we just added to our detail page? We also want to add the same change there, so other people won't be able to edit existing posts. +Remember the edit icon we just added to our detail page? We also want to add the same change there, so other people won't be able to edit existing blog posts. Open `blog/templates/blog/post_detail.html` in the code editor and find this line: {% filename %}blog/templates/blog/post_detail.html{% endfilename %} ```html - + ``` Change it to this: @@ -390,7 +390,7 @@ Change it to this: {% filename %}blog/templates/blog/post_detail.html{% endfilename %} ```html {% if user.is_authenticated %} - + {% endif %} ``` diff --git a/en/django_models/README.md b/en/django_models/README.md index cab8f68e264..98e9dcb1147 100644 --- a/en/django_models/README.md +++ b/en/django_models/README.md @@ -39,7 +39,7 @@ We need to answer the question: What is a blog post? What properties should it h Well, for sure our blog post needs some text with its content and a title, right? It would be also nice to know who wrote it – so we need an author. Finally, we want to know when the post was created and published. ``` -Post +BlogPost -------- title text @@ -130,7 +130,7 @@ from django.db import models from django.utils import timezone -class Post(models.Model): +class BlogPost(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) title = models.CharField(max_length=200) text = models.TextField() @@ -151,10 +151,10 @@ It looks scary, right? But don't worry – we will explain what these lines mean All lines starting with `from` or `import` are lines that add some bits from other files. So instead of copying and pasting the same things in every file, we can include some parts with `from ... import ...`. -`class Post(models.Model):` – this line defines our model (it is an `object`). +`class BlogPost(models.Model):` – this line defines our model (it is an `object`). - `class` is a special keyword that indicates that we are defining an object. -- `Post` is the name of our model. We can give it a different name (but we must avoid special characters and whitespace). Always start a class name with an uppercase letter. -- `models.Model` means that the Post is a Django Model, so Django knows that it should be saved in the database. +- `BlogPost` is the name of our model. We can give it a different name (but we must avoid special characters and whitespace). Always start a class name with an uppercase letter. +- `models.Model` means that the BlogPost is a Django Model, so Django knows that it should be saved in the database. Now we define the properties we were talking about: `title`, `text`, `created_date`, `published_date` and `author`. To do that we need to define the type of each field (Is it text? A number? A date? A relation to another object, like a User?) @@ -167,7 +167,7 @@ We will not explain every bit of code here since it would take too much time. Yo What about `def publish(self):`? This is exactly the `publish` method we were talking about before. `def` means that this is a function/method and `publish` is the name of the method. You can change the name of the method if you want. The naming rule is that we use lowercase and underscores instead of spaces. For example, a method that calculates average price could be called `calculate_average_price`. -Methods often `return` something. There is an example of that in the `__str__` method. In this scenario, when we call `__str__()` we will get a text (**string**) with a Post title. +Methods often `return` something. There is an example of that in the `__str__` method. In this scenario, when we call `__str__()` we will get a text (**string**) with a BlogPost title. Also notice that both `def publish(self):` and `def __str__(self):` are indented inside our class. Because Python is sensitive to whitespace, we need to indent our methods inside the class. Otherwise, the methods won't belong to the class, and you can get some unexpected behavior. @@ -182,7 +182,7 @@ The last step here is to add our new model to our database. First we have to mak (myvenv) ~/djangogirls$ python manage.py makemigrations blog Migrations for 'blog': blog/migrations/0001_initial.py: - - Create model Post + - Create model BlogPost ``` **Note:** Remember to save the files you edit. Otherwise, your computer will execute the previous version which might give you unexpected error messages. @@ -198,4 +198,4 @@ Running migrations: Applying blog.0001_initial... OK ``` -Hurray! Our Post model is now in our database! It would be nice to see it, right? Jump to the next chapter to see what your Post looks like! +Hurray! Our BlogPost model is now in our database! It would be nice to see it, right? Jump to the next chapter to see what your BlogPost looks like! diff --git a/en/django_orm/README.md b/en/django_orm/README.md index 54ec2a1d2ae..7c1218539d4 100644 --- a/en/django_orm/README.md +++ b/en/django_orm/README.md @@ -32,41 +32,41 @@ You're now in Django's interactive console. It's just like the Python prompt, bu ### All objects -Let's try to display all of our posts first. You can do that with the following command: +Let's try to display all of our blog posts first. You can do that with the following command: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.all() +>>> BlogPost.objects.all() Traceback (most recent call last): File "", line 1, in -NameError: name 'Post' is not defined +NameError: name 'BlogPost' is not defined ``` -Oops! An error showed up. It tells us that there is no Post. It's correct – we forgot to import it first! +Oops! An error showed up. It tells us that there is no BlogPost. It's correct – we forgot to import it first! {% filename %}command-line{% endfilename %} ```python ->>> from blog.models import Post +>>> from blog.models import BlogPost ``` -We import the model `Post` from `blog.models`. Let's try displaying all posts again: +We import the model `BlogPost` from `blog.models`. Let's try displaying all blog posts again: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.all() -, ]> +>>> BlogPost.objects.all() +, ]> ``` -This is a list of the posts we created earlier! We created these posts using the Django admin interface. But now we want to create new posts using Python, so how do we do that? +This is a list of the blog posts we created earlier! We created these blog posts using the Django admin interface. But now we want to create new blog posts using Python, so how do we do that? ### Create object -This is how you create a new Post object in database: +This is how you create a new BlogPost object in database: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.create(author=me, title='Sample title', text='Test') +>>> BlogPost.objects.create(author=me, title='Sample title', text='Test') ``` But we have one missing ingredient here: `me`. We need to pass an instance of `User` model as an author. How do we do that? @@ -95,79 +95,79 @@ This is the superuser we created earlier! Let's get an instance of the user now As you can see, we now `get` a `User` with a `username` that equals 'ola'. Neat! -Now we can finally create our post: +Now we can finally create our blog post: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.create(author=me, title='Sample title', text='Test') - +>>> BlogPost.objects.create(author=me, title='Sample title', text='Test') + ``` Hurray! Wanna check if it worked? {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.all() -, , ]> +>>> BlogPost.objects.all() +, , ]> ``` -There it is, one more post in the list! +There it is, one more blog post in the list! -### Add more posts +### Add more blog posts -You can now have a little fun and add more posts to see how it works. Add two or three more and then go ahead to the next part. +You can now have a little fun and add more blog posts to see how it works. Add two or three more and then go ahead to the next part. ### Filter objects -A big part of QuerySets is the ability to filter them. Let's say we want to find all posts that user ola authored. We will use `filter` instead of `all` in `Post.objects.all()`. In parentheses we state what condition(s) a blog post needs to meet to end up in our queryset. In our case, the condition is that `author` should be equal to `me`. The way to write it in Django is `author=me`. Now our piece of code looks like this: +A big part of QuerySets is the ability to filter them. Let's say we want to find all blog posts that user ola authored. We will use `filter` instead of `all` in `BlogPost.objects.all()`. In parentheses we state what condition(s) a blog post needs to meet to end up in our queryset. In our case, the condition is that `author` should be equal to `me`. The way to write it in Django is `author=me`. Now our piece of code looks like this: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.filter(author=me) -, , , ]> +>>> BlogPost.objects.filter(author=me) +, , , ]> ``` -Or maybe we want to see all the posts that contain the word 'title' in the `title` field? +Or maybe we want to see all the blog posts that contain the word 'title' in the `title` field? {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.filter(title__contains='title') -, ]> +>>> BlogPost.objects.filter(title__contains='title') +, ]> ``` > **Note** There are two underscore characters (`_`) between `title` and `contains`. Django's ORM uses this rule to separate field names ("title") and operations or filters ("contains"). If you use only one underscore, you'll get an error like "FieldError: Cannot resolve keyword title_contains". -You can also get a list of all published posts. We do this by filtering all the posts that have `published_date` set in the past: +You can also get a list of all published blog posts. We do this by filtering all the blog posts that have `published_date` set in the past: {% filename %}command-line{% endfilename %} ```python >>> from django.utils import timezone ->>> Post.objects.filter(published_date__lte=timezone.now()) +>>> BlogPost.objects.filter(published_date__lte=timezone.now()) ``` -Unfortunately, the post we added from the Python console is not published yet. But we can change that! First get an instance of a post we want to publish: +Unfortunately, the blog post we added from the Python console is not published yet. But we can change that! First get an instance of a blog post we want to publish: {% filename %}command-line{% endfilename %} ```python ->>> post = Post.objects.get(title="Sample title") +>>> blogpost = BlogPost.objects.get(title="Sample title") ``` And then publish it with our `publish` method: {% filename %}command-line{% endfilename %} ```python ->>> post.publish() +>>> blogpost.publish() ``` -Now try to get list of published posts again (press the up arrow key three times and hit `enter`): +Now try to get list of published blog posts again (press the up arrow key three times and hit `enter`): {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.filter(published_date__lte=timezone.now()) -]> +>>> BlogPost.objects.filter(published_date__lte=timezone.now()) +]> ``` @@ -177,30 +177,30 @@ QuerySets also allow you to order the list of objects. Let's try to order them b {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.order_by('created_date') -, , , ]> +>>> BlogPost.objects.order_by('created_date') +, , , ]> ``` We can also reverse the ordering by adding `-` at the beginning: {% filename %}command-line{% endfilename %} ```python ->>> Post.objects.order_by('-created_date') -, , , ]> +>>> BlogPost.objects.order_by('-created_date') +, , , ]> ``` ### Complex queries through method-chaining -As you saw, some methods on `Post.objects` return a QuerySet. +As you saw, some methods on `BlogPost.objects` return a QuerySet. The same methods can in turn also be called on a QuerySet, and will then return a new QuerySet. Thus, you can combine their effect by **chaining** them together: ```python ->>> Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date') -, , , ]> +>>> BlogPost.objects.filter(published_date__lte=timezone.now()).order_by('published_date') +, , , ]> ``` This is really powerful and lets you write quite complex queries. diff --git a/en/django_templates/README.md b/en/django_templates/README.md index 7a22f03618f..a97ee7dad8c 100644 --- a/en/django_templates/README.md +++ b/en/django_templates/README.md @@ -8,18 +8,18 @@ You see, in HTML, you can't really write Python code, because browsers don't und __Django template tags__ allow us to transfer Python-like things into HTML, so you can build dynamic websites faster. Cool! -## Display post list template +## Display blog post list template -In the previous chapter we gave our template a list of posts in the `posts` variable. Now we will display it in HTML. +In the previous chapter we gave our template a list of blog posts in the `blogposts` variable. Now we will display it in HTML. To print a variable in Django templates, we use double curly brackets with the variable's name inside, like this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html -{{ posts }} +{{ blogposts }} ``` -Try this in your `blog/templates/blog/post_list.html` template. Open it up in the code editor, and replace everything from the second `
` to the third `
` with `{{ posts }}`. Save the file, and refresh the page to see the results: +Try this in your `blog/templates/blog/post_list.html` template. Open it up in the code editor, and replace everything from the second `
` to the third `
` with `{{ blogposts }}`. Save the file, and refresh the page to see the results: ![Figure 13.1](images/step1.png) @@ -27,15 +27,15 @@ As you can see, all we've got is this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html -, ]> +, ]> ``` This means that Django understands it as a list of objects. Remember from __Introduction to Python__ how we can display lists? Yes, with for loops! In a Django template you do them like this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html -{% for post in posts %} - {{ post }} +{% for blogpost in blogposts %} + {{ blogpost }} {% endfor %} ``` @@ -43,7 +43,7 @@ Try this in your template. ![Figure 13.2](images/step2.png) -It works! But we want the posts to be displayed like the static posts we created earlier in the __Introduction to HTML__ chapter. You can mix HTML and template tags. Our `body` will look like this: +It works! But we want the blog posts to be displayed like the static posts we created earlier in the __Introduction to HTML__ chapter. You can mix HTML and template tags. Our `body` will look like this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html @@ -51,11 +51,11 @@ It works! But we want the posts to be displayed like the static posts we created

Django Girls Blog

-{% for post in posts %} +{% for blogpost in blogposts %}
-

published: {{ post.published_date }}

-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

published: {{ blogpost.published_date }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} ``` @@ -64,7 +64,7 @@ It works! But we want the posts to be displayed like the static posts we created ![Figure 13.3](images/step3.png) -Have you noticed that we used a slightly different notation this time (`{{ post.title }}` or `{{ post.text }}`)? We are accessing data in each of the fields defined in our `Post` model. Also, the `|linebreaksbr` is piping the posts' text through a filter to convert line-breaks into paragraphs. +Have you noticed that we used a slightly different notation this time (`{{ blogpost.title }}` or `{{ blogpost.text }}`)? We are accessing data in each of the fields defined in our `BlogPost` model. Also, the `|linebreaksbr` is piping the blog posts' text through a filter to convert line-breaks into paragraphs. ## One more thing @@ -80,7 +80,7 @@ $ git status $ git add . $ git status [...] -$ git commit -m "Modified templates to display posts from database." +$ git commit -m "Modified templates to display blog posts from database." [...] $ git push ``` @@ -96,10 +96,10 @@ $ git pull (Remember to substitute `` with your actual PythonAnywhere subdomain, without the angle-brackets.) -* Finally, hop on over to the ["Web" page](https://www.pythonanywhere.com/web_app_setup/) and hit **Reload** on your web app. (To reach other PythonAnywhere pages from the console, use the menu button in the upper right corner.) Your update should be live on https://subdomain.pythonanywhere.com -- check it out in the browser! If the blog posts on your PythonAnywhere site don't match the posts appearing on the blog hosted on your local server, that's OK. The databases on your local computer and Python Anywhere don't sync with the rest of your files. +* Finally, hop on over to the ["Web" page](https://www.pythonanywhere.com/web_app_setup/) and hit **Reload** on your web app. (To reach other PythonAnywhere pages from the console, use the menu button in the upper right corner.) Your update should be live on https://subdomain.pythonanywhere.com -- check it out in the browser! If the blog posts on your PythonAnywhere site don't match the blog posts appearing on the blog hosted on your local server, that's OK. The databases on your local computer and Python Anywhere don't sync with the rest of your files. -Congrats! Now go ahead and try adding a new post in your Django admin (remember to add published_date!) Make sure you are in the Django admin for your pythonanywhere site, https://subdomain.pythonanywhere.com/admin. Then refresh your page to see if the post appears there. +Congrats! Now go ahead and try adding a new blog post in your Django admin (remember to add published_date!) Make sure you are in the Django admin for your pythonanywhere site, https://subdomain.pythonanywhere.com/admin. Then refresh your page to see if the blog post appears there. Works like a charm? We're proud! Step away from your computer for a bit – you have earned a break. :) diff --git a/en/django_urls/README.md b/en/django_urls/README.md index 25c680513fe..0c181d9043f 100644 --- a/en/django_urls/README.md +++ b/en/django_urls/README.md @@ -45,7 +45,7 @@ This line means that for every URL that starts with `admin/`, Django will find a ## Your first Django URL! -Time to create our first URL! We want 'http://127.0.0.1:8000/' to be the home page of our blog and to display a list of posts. +Time to create our first URL! We want 'http://127.0.0.1:8000/' to be the home page of our blog and to display a list of blog posts. We also want to keep the `mysite/urls.py` file clean, so we will import URLs from our `blog` application to the main `mysite/urls.py` file. diff --git a/en/dynamic_data_in_templates/README.md b/en/dynamic_data_in_templates/README.md index d297f4ed3db..1391ddc3b25 100644 --- a/en/dynamic_data_in_templates/README.md +++ b/en/dynamic_data_in_templates/README.md @@ -1,6 +1,6 @@ # Dynamic data in templates -We have different pieces in place: the `Post` model is defined in `models.py`, we have `post_list` in `views.py` and the template added. But how will we actually make our posts appear in our HTML template? Because that is what we want to do – take some content (models saved in the database) and display it nicely in our template, right? +We have different pieces in place: the `BlogPost` model is defined in `models.py`, we have `post_list` in `views.py` and the template added. But how will we actually make our blog posts appear in our HTML template? Because that is what we want to do – take some content (models saved in the database) and display it nicely in our template, right? This is exactly what *views* are supposed to do: connect models and templates. In our `post_list` *view* we will need to take the models we want to display and pass them to the template. In a *view* we decide what (model) will be displayed in a template. @@ -16,17 +16,17 @@ def post_list(request): return render(request, 'blog/post_list.html', {}) ``` -Remember when we talked about including code written in different files? Now is the moment when we have to include the model we have written in `models.py`. We will add the line `from .models import Post` like this: +Remember when we talked about including code written in different files? Now is the moment when we have to include the model we have written in `models.py`. We will add the line `from .models import BlogPost` like this: {% filename %}blog/views.py{% endfilename %} ```python from django.shortcuts import render -from .models import Post +from .models import BlogPost ``` -The dot before `models` means *current directory* or *current application*. Both `views.py` and `models.py` are in the same directory. This means we can use `.` and the name of the file (without `.py`). Then we import the name of the model (`Post`). +The dot before `models` means *current directory* or *current application*. Both `views.py` and `models.py` are in the same directory. This means we can use `.` and the name of the file (without `.py`). Then we import the name of the model (`BlogPost`). -But what's next? To take actual blog posts from the `Post` model we need something called `QuerySet`. +But what's next? To take actual blog posts from the `BlogPost` model we need something called `QuerySet`. ## QuerySet @@ -36,7 +36,7 @@ So now we want published blog posts sorted by `published_date`, right? We alread {% filename %}blog/views.py{% endfilename %} ```python -Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date') +BlogPost.objects.filter(published_date__lte=timezone.now()).order_by('published_date') ``` So, let's open the `blog/views.py` file in the code editor, and add this piece of code to the function `def post_list(request)` -- but don't forget to first add `from django.utils import timezone`: @@ -45,20 +45,20 @@ So, let's open the `blog/views.py` file in the code editor, and add this piece o ```python from django.shortcuts import render from django.utils import timezone -from .models import Post +from .models import BlogPost def post_list(request): - posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date') + blogposts = BlogPost.objects.filter(published_date__lte=timezone.now()).order_by('published_date') return render(request, 'blog/post_list.html', {}) ``` To display our QuerySet on our blog's post list, we have two things left to do: -1. Pass the `posts` QuerySet to the template context, by changing the `render` function call. We'll do this now. -2. Modify the template to display the `posts` QuerySet. We'll cover this in a later chapter. +1. Pass the `blogposts` QuerySet to the template context, by changing the `render` function call. We'll do this now. +2. Modify the template to display the `blogposts` QuerySet. We'll cover this in a later chapter. -Please note that we create a *variable* for our QuerySet: `posts`. Treat this as the name of our QuerySet. From now on we can refer to it by this name. +Please note that we create a *variable* for our QuerySet: `blogposts`. Treat this as the name of our QuerySet. From now on we can refer to it by this name. -In the `render` function we have one parameter `request` (everything we receive from the user via the Internet) and another giving the template file (`'blog/post_list.html'`). The last parameter, `{}`, is a place in which we can add some things for the template to use. We need to give them names (we will stick to `'posts'` right now). :) It should look like this: `{'posts': posts}`. Please note that the part before `:` is a string; you need to wrap it with quotes: `''`. +In the `render` function we have one parameter `request` (everything we receive from the user via the Internet) and another giving the template file (`'blog/post_list.html'`). The last parameter, `{}`, is a place in which we can add some things for the template to use. We need to give them names (we will stick to `'blogposts'` right now). :) It should look like this: `{'blogposts': blogposts}`. Please note that the part before `:` is a string; you need to wrap it with quotes: `''`. So finally our `blog/views.py` file should look like this: @@ -66,11 +66,11 @@ So finally our `blog/views.py` file should look like this: ```python from django.shortcuts import render from django.utils import timezone -from .models import Post +from .models import BlogPost def post_list(request): - posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date') - return render(request, 'blog/post_list.html', {'posts': posts}) + blogposts = BlogPost.objects.filter(published_date__lte=timezone.now()).order_by('published_date') + return render(request, 'blog/post_list.html', {'blogposts': blogposts}) ``` That's it! Time to go back to our template and display this QuerySet! diff --git a/en/extend_your_application/README.md b/en/extend_your_application/README.md index 5a9cf8cbc35..23fc6d558f7 100644 --- a/en/extend_your_application/README.md +++ b/en/extend_your_application/README.md @@ -6,11 +6,11 @@ We've already completed all the different steps necessary for the creation of ou Time to practice! -The first thing we need in our blog is, obviously, a page to display one post, right? +The first thing we need in our blog is, obviously, a page to display one blog post, right? -We already have a `Post` model, so we don't need to add anything to `models.py`. +We already have a `BlogPost` model, so we don't need to add anything to `models.py`. -## Create a template link to a post's detail +## Create a template link to a blog post's detail We will start with adding a link inside `blog/templates/blog/post_list.html` file. Open it in the code editor, and so far it should look like this: {% filename %}blog/templates/blog/post_list.html{% endfilename %} @@ -18,40 +18,40 @@ We will start with adding a link inside `blog/templates/blog/post_list.html` fil {% extends 'blog/base.html' %} {% block content %} - {% for post in posts %} + {% for blogpost in blogposts %}
- {{ post.published_date }} + {{ blogpost.published_date }}
-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} {% endblock %} ``` -{% raw %}We want to have a link from a post's title in the post list to the post's detail page. Let's change `

{{ post.title }}

` so that it links to the post's detail page:{% endraw %} +{% raw %}We want to have a link from a blog post's title in the post list to the blog post's detail page. Let's change `

{{ blogpost.title }}

` so that it links to the blog post's detail page:{% endraw %} {% filename %}{{ warning_icon }} blog/templates/blog/post_list.html{% endfilename %} ```html -

{{ post.title }}

+

{{ blogpost.title }}

``` -{% raw %}Time to explain the mysterious `{% url 'post_detail' pk=post.pk %}`. As you might suspect, the `{% %}` notation means that we are using Django template tags. This time we will use one that will create a URL for us!{% endraw %} +{% raw %}Time to explain the mysterious `{% url 'post_detail' pk=blogpost.pk %}`. As you might suspect, the `{% %}` notation means that we are using Django template tags. This time we will use one that will create a URL for us!{% endraw %} The `post_detail` part means that Django will be expecting a URL in `blog/urls.py` with name=post_detail -And how about `pk=post.pk`? `pk` is short for primary key, which is a unique identifier for each record in a database. Every Django model has a field which serves as its primary key, and whatever other name it has, it can also be referred to as "pk". Because we didn't specify a primary key in our `Post` model, Django creates one for us (by default, a field named "id" holding a number that increases for each record, i.e. 1, 2, 3) and adds it as a field to each of our posts. We access the primary key by writing `post.pk`, the same way we access other fields (`title`, `author`, etc.) in our `Post` object! +And how about `pk=blogpost.pk`? `pk` is short for primary key, which is a unique identifier for each record in a database. Every Django model has a field which serves as its primary key, and whatever other name it has, it can also be referred to as "pk". Because we didn't specify a primary key in our `BlogPost` model, Django creates one for us (by default, a field named "id" holding a number that increases for each record, i.e. 1, 2, 3) and adds it as a field to each of our blog posts. We access the primary key by writing `blogpost.pk`, the same way we access other fields (`title`, `author`, etc.) in our `BlogPost` object! Now when we go to http://127.0.0.1:8000/ we will have an error (as expected, since we do not yet have a URL or a *view* for `post_detail`). It will look like this: ![NoReverseMatch error](images/no_reverse_match2.png) -## Create a URL to a post's detail +## Create a URL to a blog post's detail Let's create a URL in `urls.py` for our `post_detail` *view*! -We want our first post's detail to be displayed at this **URL**: http://127.0.0.1:8000/post/1/ +We want our first blog post's detail to be displayed at this **URL**: http://127.0.0.1:8000/post/1/ Let's make a URL in the `blog/urls.py` file to point Django to a *view* named `post_detail`, that will show an entire blog post. Open the `blog/urls.py` file in the code editor, and add the line `path('post//', views.post_detail, name='post_detail'),` so that the file looks like this: @@ -79,7 +79,7 @@ OK, we've added a new URL pattern to `blog/urls.py`! Let's refresh the page: htt Do you remember what the next step is? It's adding a view! -## Add a post's detail view +## Add a blog post's detail view This time our *view* is given an extra parameter, `pk`. Our *view* needs to catch it, right? So we will define our function as `def post_detail(request, pk):`. Note that this parameter must have the exact same name as the one we specified in `urls` (`pk`). Also note that omitting this variable is incorrect and will result in an error! @@ -88,14 +88,14 @@ Now, we want to get one and only one blog post. To do this, we can use querysets {% filename %}{{ warning_icon }} blog/views.py{% endfilename %} ```python -Post.objects.get(pk=pk) +BlogPost.objects.get(pk=pk) ``` -But this code has a problem. If there is no `Post` with the given `primary key` (`pk`) we will have a super ugly error! +But this code has a problem. If there is no `BlogPost` with the given `primary key` (`pk`) we will have a super ugly error! ![DoesNotExist error](images/does_not_exist2.png) -We don't want that! But luckily Django comes with something that will handle that for us: `get_object_or_404`. In case there is no `Post` with the given `pk`, it will display much nicer page, the `Page Not Found 404` page. +We don't want that! But luckily Django comes with something that will handle that for us: `get_object_or_404`. In case there is no `BlogPost` with the given `pk`, it will display much nicer page, the `Page Not Found 404` page. ![Page not found](images/404_2.png) @@ -117,13 +117,13 @@ And at the end of the file we will add our *view*: {% filename %}blog/views.py{% endfilename %} ```python def post_detail(request, pk): - post = get_object_or_404(Post, pk=pk) - return render(request, 'blog/post_detail.html', {'post': post}) + blogpost = get_object_or_404(BlogPost, pk=pk) + return render(request, 'blog/post_detail.html', {'blogpost': blogpost}) ``` Yes. It is time to refresh the page: http://127.0.0.1:8000/ -![Post list view](images/post_list2.png) +![BlogPost list view](images/post_list2.png) It worked! But what happens when you click a link in blog post title? @@ -131,7 +131,7 @@ It worked! But what happens when you click a link in blog post title? Oh no! Another error! But we already know how to deal with it, right? We need to add a template! -## Create a template for the post details +## Create a template for the blog post details We will create a file in `blog/templates/blog` called `post_detail.html`, and open it in the code editor. @@ -143,24 +143,24 @@ Enter the following code: {% block content %}
- {% if post.published_date %} + {% if blogpost.published_date %}
- {{ post.published_date }} + {{ blogpost.published_date }}
{% endif %} -

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endblock %} ``` -Once again we are extending `base.html`. In the `content` block we want to display a post's published_date (if it exists), title and text. But we should discuss some important things, right? +Once again we are extending `base.html`. In the `content` block we want to display a blog post's published_date (if it exists), title and text. But we should discuss some important things, right? -{% raw %}`{% if ... %} ... {% endif %}` is a template tag we can use when we want to check something. (Remember `if ... else ...` from __Introduction to Python__ chapter?) In this scenario we want to check if a post's `published_date` is not empty.{% endraw %} +{% raw %}`{% if ... %} ... {% endif %}` is a template tag we can use when we want to check something. (Remember `if ... else ...` from __Introduction to Python__ chapter?) In this scenario we want to check if a blog post's `published_date` is not empty.{% endraw %} OK, we can refresh our page and see if `TemplateDoesNotExist` is gone now. -![Post detail page](images/post_detail2.png) +![BlogPost detail page](images/post_detail2.png) Yay! It works! diff --git a/en/html/README.md b/en/html/README.md index 37939cf76fa..4bfbc99427c 100644 --- a/en/html/README.md +++ b/en/html/README.md @@ -148,7 +148,7 @@ It gives us this effect: Yaaay! But so far, our template only ever displays exactly __the same information__ – whereas earlier we were talking about templates as allowing us to display __different__ information in the __same format__. -What we really want to do is display real posts added in our Django admin – and that's where we're going next. +What we really want to do is display real blog posts added in our Django admin – and that's where we're going next. ## One more thing: deploy! diff --git a/en/template_extending/README.md b/en/template_extending/README.md index 1618809d415..e719b66bea0 100644 --- a/en/template_extending/README.md +++ b/en/template_extending/README.md @@ -39,13 +39,13 @@ Then open it up in the code editor and copy everything from `post_list.html` to
- {% for post in posts %} + {% for blogpost in blogposts %}
- {{ post.published_date }} + {{ blogpost.published_date }}
-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %}
@@ -74,7 +74,7 @@ Then in `base.html`, replace your whole `` (everything between `` an ``` -{% raw %}You might notice this replaced everything from `{% for post in posts %}` to `{% endfor %}` with: {% endraw %} +{% raw %}You might notice this replaced everything from `{% for blogpost in blogposts %}` to `{% endfor %}` with: {% endraw %} {% filename %}blog/templates/blog/base.html{% endfilename %} ```html @@ -84,17 +84,17 @@ Then in `base.html`, replace your whole `` (everything between `` an But why? You just created a `block`! You used the template tag `{% block %}` to make an area that will have HTML inserted in it. That HTML will come from another template that extends this template (`base.html`). We will show you how to do this in a moment. Now save `base.html` and open your `blog/templates/blog/post_list.html` again in the code editor. -{% raw %}You're going to remove everything above `{% for post in posts %}` and below `{% endfor %}`. When you're done, the file will look like this:{% endraw %} +{% raw %}You're going to remove everything above `{% for blogpost in blogposts %}` and below `{% endfor %}`. When you're done, the file will look like this:{% endraw %} {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html -{% for post in posts %} +{% for blogpost in blogposts %}
- {{ post.published_date }} + {{ blogpost.published_date }}
-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} ``` @@ -107,13 +107,13 @@ Time to add block tags to this file! {% filename %}blog/templates/blog/post_list.html{% endfilename %} ```html {% block content %} - {% for post in posts %} + {% for blogpost in blogposts %}
- {{ post.published_date }} + {{ blogpost.published_date }}
-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} {% endblock %} @@ -126,13 +126,13 @@ Only one thing left. We need to connect these two templates together. This is w {% extends 'blog/base.html' %} {% block content %} - {% for post in posts %} + {% for blogpost in blogposts %}
- {{ post.published_date }} + {{ blogpost.published_date }}
-

{{ post.title }}

-

{{ post.text|linebreaksbr }}

+

{{ blogpost.title }}

+

{{ blogpost.text|linebreaksbr }}

{% endfor %} {% endblock %}