---
# System prepended metadata

title: Тема 8. Bootstrap
tags: [Python Web]

---

---
tags: Python Web
source: Django 2.0 для начинающих, Глава 10, с.210
---

# Тема 8. Bootstrap

https://github.com/roman-yatsenko/django-topics/tree/main/news

## Приложение Pages

```shell
python manage.py startapp pages
```

### `news_project\settings.py`

```python=33
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'pages' # new
]
```

### `news_project\urls.py`

```python=16
from django.contrib import admin
from django.urls import path, include # delete next import

urlpatterns = [
    path('', include('pages.urls')), # change
    path('admin/', admin.site.urls),
    path('users/', include('users.urls')),
    path('users/', include('django.contrib.auth.urls')),
]
```

```shell
touch pages/urls.py
```

### `pages\urls.py`

```python=
from django.urls import path

from . import views

urlpatterns = [
    path('', views.HomePageView.as_view(), name='home'),
]
```

### `pages\views.py`

```python=
from django.views.generic import TemplateView


class HomePageView(TemplateView):
    template_name = 'home.html'
```

```shell
python manage.py runserver
```

## Тесты

### `pages\tests.py`

```python=
from django.contrib.auth import get_user_model
from django.test import SimpleTestCase, TestCase
from django.urls import reverse


class HomePageTests(SimpleTestCase):

    def test_home_page_status_code(self):
        response = self.client.get('/')
        self.assertEqual(response.status_code, 200)
    
    def test_view_url_by_name(self):
        response = self.client.get(reverse('home'))
        self.assertEqual(response.status_code, 200)
    
    def test_view_uses_correct_template(self):
        response = self.client.get(reverse('home'))
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'home.html')


class SignupPageTests(TestCase):
    username = 'newuser'
    email = 'newuser@email.com'
    
    def test_signup_page_status_code(self):
        response = self.client.get('/users/signup/')
        self.assertEqual(response.status_code, 200)
    
    def test_view_url_by_name(self):
        response = self.client.get(reverse('signup'))
        self.assertEqual(response.status_code, 200)
    
    def test_view_uses_correct_template(self):
        response = self.client.get(reverse('signup'))
        self.assertEqual(response.status_code, 200)
        self.assertTemplateUsed(response, 'signup.html')
    
    def test_signup_form(self):
        new_user = get_user_model().objects.create_user(self.username, self.email)
        self.assertEqual(get_user_model().objects.all().count(), 1)
        self.assertEqual(get_user_model().objects.all()[0].username, self.username)
        self.assertEqual(get_user_model().objects.all()[0].email, self.email)
```

```shell
python manage.py test
```

## Bootstrap

https://getbootstrap.com/docs/4.6/getting-started/introduction/

### `templates\base.html`

```htmlmixed=
<!doctype html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/css/bootstrap.min.css" integrity="sha384-B0vP5xmATw1+K9KRQjQERJvTumQW0nPEzvF6L/Z6nronJ3oUOFUFpCjEUQouq2+l" crossorigin="anonymous">

    <title>Newspaper App</title>
  </head>
  <body>
    
    {% block content %}
    {% endblock %}

    <!-- Optional JavaScript; choose one of the two! -->

    <!-- Option 1: jQuery and Bootstrap Bundle (includes Popper) -->
    <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-Piv4xVNRyMGpqkS2by6br4gNJ7DXjqk09RmUpJ8jgGtD7zP9yug3goQfGII0yAns" crossorigin="anonymous"></script>

  </body>
</html>
```

```shell=
python manage.py runserver
```

## NavBar

https://getbootstrap.com/docs/4.6/components/navbar/#supported-content

### `templates\base.html`

```htmlmixed=13
  <body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark mb-4">
      <a class="navbar-brand" href="{% url 'home' %}">Newspaper</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>
    
      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        {% if user.is_authenticated%}
          <ul class="navbar-nav ml-auto">
            <li class="nav-item dropdown">
              <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                {{ user.username }}
              </a>
              <div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
                <a class="dropdown-item" href="{% url 'password_change' %}">Change password</a>
                <div class="dropdown-divider"></div>
                <a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
              </div>
            </li>
          </ul>
        {% else %}
          <form class="form-inline ml-auto">
            <a class="btn btn-outline-secondary" href="{% url 'login' %}">Log in</a>
            <a class="btn btn-primary ml-2" href="{% url 'signup' %}">Sign Up</a>
          </form>
        {% endif %}
      </div>
    </nav>
    <div class="container">
      {% block content %}
      {% endblock %}
    </div>
```

### `templates\registration\login.html`

```htmlmixed=10
  <button class="btn btn-success ml-2" type="submit">Login</button>
```

## Signup Form

```shell
pipenv install django-crispy-forms
```

### `news_project\settings.py`

```python=33
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    
    # 3rd Party
    'crispy_forms',
    
    # Local
    'users',
    'pages',
]
```

```python=135
CRISPY_TEMPLATE_PACK = 'bootstrap4'
```

### `templates\signup.html`

```htmlmixed=
{% extends 'base.html' %}

{% load crispy_forms_tags %}

{% block title %}Sign Up{% endblock %}

{% block content %}
<h2>Sign up</h2>
<form method="post">
  {% csrf_token %}
  {{ form|crispy }}
  <button class="btn btn-success" type="submit">Sign up</button>
</form>
{% endblock %}
```

```shell=
python manage.py runserver
```

---

(c) Яценко Р.Н., 2021

[Учебный центр компьютерных технологий "Кит"](http://kit.kh.ua/)

<img src="https://i.imgur.com/Kh901c1.png" style="width: 150px; position: fixed; top: 100px; right: 10px; border: 0; box-shadow: none;">
