🐍🚀 Django с нуля. Часть 2: регистрация, авторизация, ограничение доступа

Во второй части туториала будем разрабатывать систему регистрации и авторизации, а также ограничим доступ к просмотру профилей для неавторизованных посетителей блога.
36
🐍🚀 Django с нуля. Часть 2: регистрация, авторизация, ограничение доступа

Второй этап разработки

Продолжим работу над проектом с создания первой учетной записи для админа, а затем перейдем к разработке необходимых моделей для базы данных и форм регистрации / авторизации. Весь код второго этапа находится здесь.

Создание учетных записей и администрирование

Панель администрирования открывается по адресу http://localhost:8000/admin. Пока что войти в админку нельзя: сначала нужно создать учетную запись суперпользователя (админа). Перед началом этого процесса следует применить миграции: python manage.py migrate (эту операцию мы намеренно пропустили на первом шаге).

После применения миграций можно приступать к созданию записи для суперпользователя: python manage.py createsuperuser

В ходе выполнения команды появится приглашение ввести имя пользователя, пароль и емейл (они не отображаются во время ввода – будьте внимательны). Учетная запись суперпользователя готова, запустите сервер и авторизуйтесь:

Учетная запись администратора
Учетная запись администратора

Кликните по ADD USER и создайте учетные записи для двух тестовых пользователей. Обратите внимание, что Джанго берет хэширование паролей на себя:

Аккаунт тестового пользователя
Аккаунт тестового пользователя
Примечание: если вы забыли пароль суперпользователя, его можно сменить так: python manage.py changepassword <имя пользователя>.

Работа с базой данных и моделями

Одна из сильнейших сторон Джанго – простота подключения базы данных и возможность работать с любыми базами с помощью одного и того же кода (и без глубоких познаний в области языка запросов SQL). Django уже создал для нас файл models.py. Каждая модель представляет отдельную таблицу в базе. Откроем models.py и напишем код для модели Post.

Для обновления структуры базы создадим и выполним миграции: сначала запустим python manage.py makemigrations, а затем python manage.py migrate.

Теперь пора обновить файл blog\views.py, в котором на первом этапе мы записали тестовый контент. Для обработки реального контента из модели Post код должен выглядеть так.

Чтобы модель для создания новых записей отображалась в панели администрирования, ее нужно зарегистрировать в файле blog\admin.py:

        from django.contrib import admin
from .models import Post
admin.site.register(Post)

    

Теперь можно открыть админку http://localhost:8000/admin/ и создать несколько постов от разных пользователей:

Записи от нескольких пользователей
Записи от нескольких пользователей

Обратите внимание на формат даты – если указание часов и минут не требуется, формат можно изменить в шаблоне home.html:

        <small class="text-muted">{{ post.date_posted|date:"F d, Y" }}</small>
    
Больше полезных материалов вы найдете на нашем телеграм-канале «Библиотека питониста»

Формы регистрации и авторизации

Как уже упоминалось, приложения из проекта можно использовать отдельно – вставить блог, например, в другой проект. Поэтому и систему регистрации / авторизации стоит реализовать отдельно. Запустим Джанго-команду для создания нового приложения под названием users: python manage.py startapp users. После этого нужно зарегистрировать новое приложение, как мы уже делали с блогом. Сначала откройте my_site\settings.py и добавьте нужную строку в список INSTALLED_APPS:

        INSTALLED_APPS = [
	'blog.apps.BlogConfig',
	'users.apps.UsersConfig',
	'crispy_forms',
	'django.contrib.admin',
	'django.contrib.auth',
	'django.contrib.contenttypes',
	'django.contrib.sessions',
	'django.contrib.messages',
	'django.contrib.staticfiles',
]

    

В этом же файле в самом низу добавьте ссылку на стили Bootstrap для форм:

        CRISPY_TEMPLATE_PACK = 'bootstrap4'
    

Модуль django-crispy-forms не входит в стандартный дистрибутив Джанго – скорее всего, вам потребуется его установить: pipenv install django-crispy-forms.

Затем откройте users\views.py и сохраните в нем код:

        from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import UserRegisterForm
def register(request):
	if request.method == 'POST':
    	form = UserRegisterForm(request.POST)
    	if form.is_valid():
        	form.save()
        	username = form.cleaned_data.get('username')
        	messages.success(request, f'Создан аккаунт {username}!')
        	return redirect('blog-home')
	else:
    	form = UserRegisterForm()
	return render(request, 'users/register.html', {'form': form})

    

Для формы регистрации нам потребуется соответствующий шаблон. Как и в случае с блогом, создайте в папке users директорию templates, в ней – поддиректорию users, а в ней файл register.html. Код шаблона будет таким.

Внесем нужные изменения в url-паттерны. Откройте my_site\urls.py и добавьте в него импорт users_views и маршрут к форме регистрации:

        from django.contrib import admin
from django.urls import path, include
from users import views as user_views
 
urlpatterns = [
	path('admin/', admin.site.urls),
	path('register/', user_views.register, name='register'),
	path('', include('blog.urls')),
]

    

Теперь создайте файл users\forms.py и сохраните в нем этот код, после чего добавьте отображение сообщения об успешной регистрации в шаблон блога base.html, прямо над блоком контента {% block content %}{% endblock %}:

        {% if messages %}
        	{% for message in messages %}
          	<div class="alert alert-{{ message.tags }}">
            	{{ message }}
          	</div>
        	{% endfor %}
{% endif %}

    

Запустим python manage.py runserver и оценим нашу форму регистрации http://localhost:8000/register/:

Форма регистрации
Форма регистрации

Зарегистрируем нового пользователя NewUser и получим сообщение об успешной регистрации аккаунта. Проверим панель администрирования http://localhost:8000/admin/auth/user/ – новая учетная запись там появилась, значит, форма работает правильно. Попробуйте ввести некорректные регистрационные данные – форма проводит валидацию самостоятельно, без всяких дополнительных усилий с нашей стороны:

Форма неплохо справляется с валидацией
Форма неплохо справляется с валидацией

Вход и доступ к профилям для зарегистрированных пользователей

Одно из главных преимуществ системы авторизации – возможность показывать определенный контент только зарегистрированным пользователям. Сначала мы создадим логику и страницы входа и выхода, а затем перейдем к вопросу ограничения доступа для неавторизованных и/или незарегистрированных пользователей.

Прежде всего добавим в файл my_site\urls.py импорт нужных модулей и маршруты к шаблонам страниц входа и выхода. Теперь нужно создать два шаблона в папке users. Сохраните этот код для login.html. Он генерирует такую страницу:

Страница входа
Страница входа

Код для logout.html генерирует страницу выхода из учетной записи:

Страница выхода
Страница выхода

Внизу файла my_site\settings.py добавьте:

        LOGIN_REDIRECT_URL = 'blog-home'
LOGIN_URL = 'login'

    

Также отредактируем код в файле users\views.py, теперь он должен выглядеть так.

Сообщение об успешной регистрации
Сообщение об успешной регистрации

Чтобы управлять профилями в админ-панели, нужно зарегистрировать модель в admin.py:

        from django.contrib import admin
from .models import Profile
admin.site.register(Profile)

    

Создайте шаблон для страницы профиля – profile.html в templates/users, и сохраните этот код в users\models.py. Код модели использует библиотеку для работы с изображениями Pillow. Установите ее в виртуальное окружение проекта: pipenv install pillow. Создайте и примените миграции:

        python manage.py makemigrations 
python manage.py migrate

    

Теперь можно запускать сервер – у нас уже есть работающие страницы http://localhost:8000/login/ и http://localhost:8000/logout/; на страницу login выполняется переадресация после регистрации. Если сейчас ввести некорректный логин, появится сообщение об ошибке. Но и ввод верных данных приведет к ошибке 404 – приложение пытается открыть профиль пользователя, но у него пока что нет правильного маршрута к странице. На самом деле, и переадресация на профиль после входа на сайт выглядит странно – гораздо логичнее перенаправить пользователя на главную страницу блога, что мы и сделаем.

Займемся переадресацией откроем my_site\settings.py и добавим переадресацию в конце файла:

        LOGIN_URL = 'login'
    

Теперь, если вы введете верный логин и пароль, произойдет переадресация на главную страницу блога. Пока что визуально это не слишком очевидно – ссылки на панели навигации не соответствуют тому, вошел ли пользователь на сайт или нет – но мы сейчас это исправим.

Прежде всего, внесем изменения в шаблон base.html – найдите правый блок навигации и вставьте этот код между тегами <div class="navbar-nav"></div>:

                  	{% if user.is_authenticated %}
            	<a class="nav-item nav-link" href="{% url 'profile' %}">Профиль</a>
            	<a class="nav-item nav-link" href="{% url 'logout' %}">Выход</a>
          	{% else %}
            	<a class="nav-item nav-link" href="{% url 'login' %}">Вход</a>
            	<a class="nav-item nav-link" href="{% url 'register' %}">Регистрация</a>
          	{% endif %}
    

Если зайти на сайт с правильным логином и паролем, можно увидеть свой профиль – правда, пока без аватарки:

Профиль без изображения пользователя
Профиль без изображения пользователя

Последнее, что мы сделаем на этом этапе – обеспечим ограничение доступа к профилям для незарегистрированных пользователей. Для этого в файл users\views.py нужно добавить декоратор @login_required:

        @login_required
def profile(request):
	return render(request, 'users/profile.html')

    

Если неавторизованный пользователь теперь попытается открыть страницу профиля, он будет переадресован на страницу входа на сайт. Напоминаем: весь код, созданный на этом этапе работы, находится здесь.

Промежуточные итоги

В этой части туториала мы реализовали систему авторизации в виде отдельного приложения, которое в дальнейшем можно будет использовать в любом другом Django-проекте. Дополнительной функциональностью для управления профилями мы займемся в следующей, заключительной части.

***

Материалы по теме

Комментарии

 
 
27 ноября 2024

[27.11.2024 0:09] Заработало в settings.py нужно было добавить crispy_bootstrap4' """INSTALLED_APPS = [

Другие приложения

'crispy_forms',
'crispy_bootstrap4',

]

CRISPY_TEMPLATE_PACK = 'bootstrap4'

[27.11.2024 0:10] И установить библиотеку pip install crispy-bootstrap4 если она не установлена

[27.11.2024 0:16] в шаблоне поменять base.html

        <a class="nav-item nav-link" href="{% url 'logout' %}">Выход</a>

на

        <form method="post" action="{% url 'logout' %}">
            {% csrf_token %}
            <!-- <button type="submit">Выход</button> -->
            <button type="submit" class="btn btn-success">Выход</button>
        </form>
11 апреля 2024

Подскажите, пожалуйста, в чем ошибка. В терминале в пайчарме ошибка выглядит так: Method Not Allowed (GET): /logout/ Method Not Allowed: /logout/ "GET /logout/ HTTP/1.1" Ничего не отображается, если перехожу по logout/

07 марта 2024

Поломали logout через Get в джанго 5. берем решения тут: https://ru.stackoverflow.com/questions/1555157/%D0%9D%D0%B5-%D0%BE%D1%82%D0%BE%D0%B1%D1%80%D0%B0%D0%B6%D0%B0%D0%B5%D1%82%D1%81%D1%8F-%D1%88%D0%B0%D0%B1%D0%BB%D0%BE%D0%BD-method-not-allowed-get-account-logout

или так можно в шаблоне поменять

            <a class="nav-item nav-link" href="{% url 'logout' %}">Выход</a>

на

            <form method="post" action="{% url 'logout' %}">
                {% csrf_token %}
                <!-- <button type="submit">Выход</button> -->
                <button type="submit" class="btn btn-success">Выход</button>
            </form>
01 января 2024

Если ошибка с формами django.template.exceptions.TemplateDoesNotExist: bootstrap4/uni_form.html

Начиная с версии django-crispy-forms 2.0, пакеты шаблонов теперь находятся в отдельных пакетах.

Вам нужно будет pip install crispy-bootstrap4 и добавить crispy_bootstrap4 в свой список INSTALLED_APPS.

04 января 2024

Можно просто передать стили в форму с помощью field.widget.attrs.update(), и не устанавливать crispy-forms.

02 июля 2023

Подскажите в чем ошибка?

02 июля 2023

если ваша функция register выглядит точно так же, как в туториале, то это косяк Crispy Forms. с последней версией Джанго нормально работает версия форм 1.14.

02 июля 2023

Понял, спасибо. Я просто решил его не использовать и думал что прокатит. Думал что это только для бутстрапа.

23 июня 2023

Разрабы, сделайте шапку скрывающуюся. Как на пикабу

19 марта 2023

Коллеги, нужна Ваша помощь. Битый час не могу найти проблему в view.py приложения users Ошибка: The view users.views.register didn't return an HttpResponse object. It returned None instead.

в urls.py проекта правки тоже внесены Текст файла, как в мануале Куда смотреть?

Итак, разобрался, возможно пригодится

  1. не знаю почему, но в рамках этого руководства нужно юзать django-crispy-forms не старше 1.14!
  2. В мануале в файле views.py не хватает блока, что если метод не POST Наверняка можно сделать красивие, но я сделал так - работает (см.пикчу)

На моменте добавления регистрации вылезает такая ошибка, в чём причина?

09 марта 2023

Эта ошибка, к сожалению, связана с новой версией Crispy Forms. Удалите установленную, установите версию не выше 1.14.0. Возможно, разработчики форм в скором времени исправят эту проблему, но пока - только так.

09 марта 2023

А как мне установить нужную версию?

09 марта 2023

Сначала удалите предыдущую версию - папки crispy_forms и django_crispy_forms-2.0, затем активируйте venv и выполните:

pip install django-crispy-forms==1.14.0
09 марта 2023

Спасибо большое

Добрый вечер, на моменте добавления регистрации возникает ошибка: Помогите пожалуйста

01 марта 2023

Пофиксил это- добавив в INSTALLED_APPS - users и 'crispy_bootstrap4', так же пришел к выводу что не очень грамотно создавать my_site и директорию my_site - логичнее создать config, а так отлично пока все легко и понятно, спасибо!

23 января 2023

подскажите, пжлйста,проделала всё по 1 занятию....у меня порожек (© Клуб Amigos 2023) стал по центру странички + сверху Главная О сообществе amigos Вход Регистрация

не выдны(отображаются белым цветом), при выделении видно синим...и немного накладывается верхняя строка на администратора)))эх...объясняю, конечно, доходчиво в кавычках)))

24 января 2023

Стили css не загружены. Попробуйте перезагрузить страницу Ctrl+F5. Если не поможет, надо проверить, видит ли Джанго папку со статикой и правильно ли она загружается в шаблоне.

24 января 2023

НА МОМЕНТЕ: Запустим python manage.py runserver и оценим нашу форму регистрации http://localhost:8000/register/:

У МЕНЯ ПОЛУЧИЛОСЬ ТАКАЯ ВОТ ОШИБКА:

25 января 2023

Скорее всего, забыли указать return для render. Что у вас в последней строке функции register в users/view.py? должно быть:

return render(request, 'users/register.html', {'form': form})

Такая ошибка возникает, если забыть return.

25 января 2023

Сердечно благодарю за помочь! Ошибку нашла! Ошибка заключалась в том, что в файле сеттингс.пай забыла указать в INSTALLED_APPS ....'crispy_forms'))))Вот, ещё такой момент, а как сделать, чтобы раделы были активными, чтобы можно было по ним переходить???

и вот такой ещё вопрос: модели же можно изменять???просто миграции у меня не выполняются почему-то.....

25 января 2023

После каждого изменения модели нужно подготовить и выполнить миграции:

manage.py makemigrations
manage.py migrate
26 января 2023

Сердечно благодарю....Всё получилось!

25 января 2023

Вы имеете в виду эти разделы? По ним можно будет переходить, если в шаблоне добавить ссылки, ведущие к соответствующим страницам.

ВОПРОС .ЧАСТЬ 2. Решила делать через категории.....всё прописала, как надо везде....загвоздка...с html файлом(не понимаю их) совсем....Смотрите, в файл base.html после nav/ написала:

{{ title }}

    <div class="row">
        <div class="col-md-12">
            {% for item in posts %}
            <div class="card mb-3">
                <div class="card-header">
                    Категория: {{ item.category }}
                </div>
                <div class="card-body">
                    <h5 class="card-title">{{ item.title }}</h5>
                    <p class="card-text">{{ item.content }}</p>
                    <a href="#" class="btn btn-primary">Read more...</a>
                </div>
                <div class="card-footer text-muted">
                    {{ item.date_posted|date:"Y-m-d H:i:s" }}
                </div>
            </div>
            {% endfor %}
        </div>
    </div>
</div>        теперь, у меня отображается как-то непонятно....я думала, что оно будет с фото пользователя, датой публикайии....но получается, категория перекрывает имя пользователя, его фото, и дату публикации.....хотела вместо  разделов вместить категории.....но не получается, перепробовала все варианты......Что же можно сделать???
26 января 2023

Чтобы вывести категорию (и в дальнейшем фильтровать посты по категориям), нужно сделать следующее:

blog/models.py:

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField()

    def __str__(self):
        return self.name

В модель Post добавить:

category = models.ForeignKey(Category, on_delete=models.CASCADE)

blog/views.py:

def posts_by_category(request, category_slug):
    category = get_object_or_404(Category, slug=category_slug)
    posts = Post.objects.filter(category=category)
    context = {
        "posts": posts
    }

    return render(request, "blog/home.html", context)

blog/urls.py:

path('category/<slug:category_slug>', views.posts_by_category, name="category"),

Шаблон home.html (или index, как он у Вас называется, но не base!):

<h5><a href="{% url 'category' post.category.slug %}">Категория: {{ post.category }}</a></h5>

И все заработает - если кликнуть по названию категории, будут выведены посты только из этой категории.

10 января 2023

Было бы классно если бы по шагам объяснялось, что происходит. В случае с формами вообще не понятно... вставьте этот код сюда, вставьте тот код туда и вжух всё работает

11 октября 2022

from django.shortcuts import render, redirect from django.contrib import messages from .forms import UserRegisterForm def register(request): if request.method == 'POST': form = UserRegisterForm(request.POST) if form.is_valid(): form.save() username = form.cleaned_data.get('username') messages.success(request, f'Создан аккаунт {username}!') return redirect('blog-home') else: form = UserRegisterForm() return render(request, 'users/register.html', {'form': form})

этот код не работает без использования декораторов как в полной версии в Гите. Прошу исправьте в коде на странице

09 октября 2022

Шикарное руководство, спасибо большое за труд ;-)

ЛУЧШИЕ СТАТЬИ ПО ТЕМЕ

LIVE >

Подпишись

на push-уведомления