/ Menu / Tasks / Blog application
In this task you will be the architect of a blog application.
Prerequisites: You need a working django project, see task Start project.
Table of contents:
- Step 1: Initialize application
- Step 2: Add app to project
- Step 3: Create model
- Step 4: Connect the Django admin site to the models
- Step 5: Create views for displaying content
- Step 6: Mapping url to view
- Step 7: Display blog posts
- Bonus 1: Modify behaviour on save
Run a django command to start an app with the name 'blog'
.
Resource: https://docs.djangoproject.com/en/4.1/intro/tutorial01/#creating-the-polls-app
Solution
To create app (from root of project on host machine):
docker compose exec clean pipenv run python manage.py startapp blog
Our new app is only a collection of files, they don't do very much at the moment.
Django isn't aware of them yet.
Tell your django project to add blog
as an app.
Hint:
See INSTALLED_APPS
in root/settings.py
.
Solution
https://docs.djangoproject.com/en/4.1/intro/tutorial02/#activating-models
INSTALLED_APPS = [
...
'blog', # <-- Add this.
]
Create a model for BlogPost
.
Be creative, identify fields you find relevant to a blogpost.
To get you started -- you should probably have a title
, and maybe an author
.
Hint:
Restart the server to create and apply migrations to the database whenever you make model changes.
Bonus: You can create more models to achieve relations.
Resources:
- https://docs.djangoproject.com/en/4.1/topics/db/models/
- See
FieldDisplay
in /solution/blog/models.py or http://localhost:8002/admin/blog/fielddisplay/
Solution
blank
: Specifies if field can be blank when creating an instance.
null
: Specifies a database constraint field can be blank when creating an instance.
# blog/models.py
from django.db import models
class Author(models.Model):
first_name = models.CharField(max_length=42, blank=True, null=True)
last_name = models.CharField(max_length=42, blank=True, null=True)
born = models.DateTimeField(blank=True, null=True)
def __str__(self) -> str:
"""
Returns the string representation of an instance.
We want to see the full name (strip whitespace if partially missing name)
"""
return f'{self.last_name} {self.last_name}'.strip()
class BlogPost(models.Model):
title = models.CharField(max_length=100, blank=False, null=True)
text = models.TextField(blank=False, null=True)
author = models.ForeignKey(Author, on_delete=models.PROTECT, blank=True, null=True)
published = models.DateTimeField()
hidden = models.BooleanField(default=False)
last_updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self) -> str:
"""
Returns the string representation of an instance.
We want to see the title.
"""
return f'{self.title}'
The admin site is an interface accessible to users with a username and password, that can be used to manage the content on the site. To log into the admin site and access all functionality you should have created a superuser in a previous step (see startproject).
Resource: https://docs.djangoproject.com/en/4.1/ref/contrib/admin/
First enter https://localhost:8001/admin/ and log in using the username and password you have defined. This will show the Django admin site/panel, but it does not contain any information on the models you have created yet. To connect the models you defined in blog/models.py register the models in blog/admin.py
as described in the resource-link.
Solution
# blog/admin.py
from django.contrib import admin
from blog.models import Author, BlogPost
# Register your models here.
admin.site.register(Author)
admin.site.register(BlogPost)
After registering the models you can re-enter the admin site. It should now be possible to add an author or write a new blog post based on the models you created.
Steps 5 to 7 will focus on Django's built in template system.
If you are more interested in an API setup, you may consider jumping to Task: Django REST framework instead.
In this section you will create a view in order to display content. Views can be function-based or class-based, in this workshop the latter is used. You can read more about the two approaches in the resource link. A view takes a web request and returns a web response, which for example can be the html content on your page.
Resource: https://docs.djangoproject.com/en/4.1/topics/class-based-views/intro/#using-class-based-views
Write a view called Home
that can be used to display some text of your choice.
Solution
# blog/views.py
from django.http import HttpResponse, HttpRequest
from django.views import View
class Home(View):
def get(self, request: HttpRequest) -> HttpResponse:
return HttpResponse('Welcome to my blog!')
To display the view at a particular url, you'll need to map a path to the view.
Use the given resources, as well as the comments in root/urls.py
and blog/urls.py
as guidance.
Resources:
Solution
# blog/urls.py
from django.urls import path
from blog.views import Home
urlpatterns = [
path('all/', Home.as_view(), name='home'),
]
# root/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
]
Now you can enter https://localhost:8001/blog/all/.
The content you displayed in the previous view isn't particularly interesting.
To display the blog posts, you can render an html page instead.
Luckily, Django has a built in template system.
You need to:
- Create an HTML-file
blog/templates/blog/home.html
and use it in your View. - Fetch all BlogPosts and give them as context to the template.
- Loop over each blogpost and render them.
Resources:
- https://docs.djangoproject.com/en/4.1/intro/tutorial03/#use-the-template-system
- https://docs.djangoproject.com/en/4.1/topics/db/queries/#retrieving-all-objects
Solution
# blog/views.py
from django.http import HttpResponse, HttpRequest
from django.views import View
from django.shortcuts import render
from blog.models import BlogPost
class Home(View):
template_name: str = 'blog/home.html'
def get(self, request: HttpRequest) -> HttpResponse:
# Fetch all BlogPosts.
blog_posts = BlogPost.objects.all()
# Give the results as context to template with name 'blog_posts'.
return render(request, self.template_name, {'blog_posts': blog_posts})
Solution
<!-- > blog/templates/blog/home.html -->
<div>
<h1>Django Blog</h1>
<p>Welcome to my super cool blog written in Django!</p>
</div>
<div>
{% for blog_post in blog_posts %}
<div>
<h2>{{ blog_post.title }}</h2>
<p>{{ blog_post.text }}</p>
</div>
{% endfor %}
</div>
If an author has last_name
equal to 'Nordmann'
, change it to 'Viking'
.
See this post: https://www.geeksforgeeks.org/overriding-the-save-method-django-models/
Solution
# blog/models.py
from django.db import models
class Author(models.Model):
...
def save(self, *args, **kwargs) -> None:
# Modify name.
if self.last_name == 'Nordmann':
self.last_name = 'Viking'
# Proceed with saving.
super().save(*args, **kwargs)
👈 Back to Tasks