Models, Views, Templates - Lesson 5
Tags: python, django, beginners
Published: 2019-06-25 09:09:27

1. [Introduction](#introduction)
2. [Templates to Views to Models](#t-v-m)
3. [Displaying From Models](#displaying-from-models)

## Introduction<a name="introduction"></a>

In the last article, we discussed how we can create a blog model in our blog app. This model contained a title, a cover image, a date field, a content field, and a slug field. Out of all these fields, we would be working on and interacting with the title, cover image and content fields. We would also be setting the date field to be automatically set to the date of the last update to our blog post, and the slug to be automatically set based on the title. I think that's a nice place to start. 🤔

Move over to your ***blog/models.py*** file and edit the field called ***raw_date***. Also add the ***save*** function to the class. 
```python
# blog/models py
from django.template.defaultfilters import slugify # a django module to take care of creating slugs
class Blog(models.Model):
...
    raw_date = models.DateTimeField(auto_now=True)
...
    def save(self, *args, **kwargs):
        self.slug = slugify(self.title)
        return super(Blog, self).save(*args, **kwargs)
...
``` 
>The parameter **auto_now**, when set to **True**, automatically sets the date to the latest date every time the model is edited and saved.
Note also, the function we added must be named **save** for this to work, because it, for lack of the right word, extends the original inbuilt save function. 

Do not forget to ***makemigrations*** and ***migrate*** your changes. Feel free to also test it to make sure it works. If you check the admin site, you would not find the date field anymore. This is because it is being automatically set. 

![Date field seems to have disappeared](https://thepracticaldev.s3.amazonaws.com/i/ftc9swldsdshi52f979f.jpg)<figcaption>Date was eaten by a hungry squirrel 🐿️</figcaption>

## Templates to Views to Models<a name="t-v-m"></a>
Don't get confused by the long heading. It's not that serious really 😉. Or is it 🤔? Just kidding. On a more serious note though, our main goal is to allow blog posts to be created from the frontend and to do this, we basically need just a form that has the fields of the model we created, with an exception of the automatically populated fields (I was going to say filled fields). So we need to create an HTML file that would hold the form (We really aren't bothering about the styling now. Feel free, though, to style however you want). 

Move over to your ***blog*** app and create a ***templates*** directory if you don't already have one. Next, create a file, I'd name mine ***add-blog***. So, ***blog/templates/add-blog.html***. Let's move over to our ***views.py*** and create the function for our add a blog page.
```python
...
# blog/views.py
from django.shortcuts import render, redirect
...
def add_blog_post(request):
    return render(request, 'add-blog.html')
```
>For now, we'd just return the html document

We need to create a ***urls.py*** file inside the blog app and link the file to the main ***urls.py*** inside the ***awesome*** directory. 
```python
# blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('add-post/', views.add_blog_post, name='add-a-post')
 ]
```
In ***awesome/urls.py***
```python
# awesome/urls.py
...
from django.urls import path, include
...
    path('counts/', views.counter, name='count-vowels'), # Don't forget this comma
    path('blog/', include('blog.urls'))
...
```
> The new line includes the blog/urls.py file in the recognized urls.

Now, go back to your html file and create the form.
```html
<!-- blog/templates/add-blog.html -->
...
<form action="{% url 'add-a-post' %}" method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <label for="title-field">Title</label>
    <input id="title-field" name="title" type="text">
    <br><br>
    <label for="cover">Cover Image</label>
    <input id="cover" name="cover-image" type="file">
    <br><br>
    <label for="blog-content">Content</label>
    <textarea id="blog-content" rows="20" cols="30" name="content"></textarea>
    <br><br>
    <input type="submit" value="Post">
</form>
...
```
> Don't forget to include your csrf_token, else it won't work 


![Image showing how form looks](https://thepracticaldev.s3.amazonaws.com/i/7r8sn8c3i2oh1hsi81wy.jpg)<figcaption>Please style if you can. This thing is ugly.</figcaption>

Now, go to ***blog/views.py*** and let's make changes to the function.

```python
# blog/views,py
...
from .models import Blog
...
def add_blog_post(request):
    if request.method == 'POST':
        blog = Blog()
        blog.title = request.POST['title']
        blog.cover = request.FILES['cover-image']
        blog.content = request.POST['content']
        blog.save()
        return redirect('home')
    return render(request, 'add-blog.html')
```
>Note: This fetches the information from the name given to each of the fields, meaning if you don't give it a name, accessing the information might be difficult.

When we run **blog.save()**, a blog post is created with the given information. Save your code, and test it. Login to the admin site and confirm that it saves.

![Image showing newly created blog post](https://thepracticaldev.s3.amazonaws.com/i/pcqyjsbrkxw3pgrrktg8.jpg)

![Image showing newly created blog post on the admin site](https://thepracticaldev.s3.amazonaws.com/i/1hnx0v358k0vhye8n2ob.jpg)

## Displaying from Models<a name="displaying-from-models"></a>
Currently when a blog post is created, we redirect the user to the home page. Not so ideal. We would want to redirect the user, however, to a page that lists all out posts. To do this, we need a page, a function and a new url entry.

***blog/views.py***
```python
# blog/views.py
...
def all_posts(requests):
    posts = Blog.objects.all()
    return render(requests, {'all-posts': posts})
...
```
***blog/templates/all-posts.html***
```html
<!-- blog/templates/all-posts.html -->
...
<body>
{% for post in all %}
    <h3>{{ post.title }}</h3>
    {{ post.date }}
    <br>
    {{ post.content }}
    <hr>
{% endfor %}
</body>
...
```
***blog/urls.py***
```python
# blog/urls.py
...
urlpatterns = [
    path('add-post/', views.add_blog_post, name='add-a-post'),
    path('all-posts', views.all_posts, name='all-posts')
 ]
...
```

This are all the changes made and their respective files. And just like this, we are able to access and display our blog posts on the frontend. 

I'd want you to, in your own free time, extend the blog model. Try adding categories using the ManytoMany field. We'd be extending the model and possibly dealing with user accounts soon. 

One thing to note is, there are other ways to handle forms in Django. This is one, and not the only way. Feel free to check around for other methods and possibly try them too. 🦉. Stay safe. Be nice. Enjoy your day.