# Формы и приложение пользователей --- ## Формы ---- Разместим форму в forms.py: ``` from django import forms from .models import Topic class TopicForm(forms.ModelForm): class Meta: model = Topic fields = ['text'] labels = {'text': ''} ``` ---- Настроим URL: ``` urlpatterns = [ ... path('new_topic/', views.new_topic, name='new_topic'), ] ``` ---- Напишем контроллер-функцию для формы: ``` from django.shortcuts import render, redirect from .models import Topic from .forms import TopicForm ... def new_topic(request): if request.method != 'POST': form = TopicForm() else: form = TopicForm(data=request.POST) if form.is_valid(): form.save() return redirect('learning_logs:topics') context = {'form': form} return render(request, 'learning_logs/new_topic.html', context) ``` ---- Создадим new_topic.html: ``` {% extends "learning_logs/base.html" %} {% block content %} <p>Add a new topic:</p> <form action="{% url 'learning_logs:new_topic' %}" method='post'> {% csrf_token %} {{ form.as_p }} <button name="submit">add topic</button> </form> {% endblock content %} ``` ---- Далее ссылка на страницу new_topic создается на странице topics: ``` {% extends "learning_logs/base.html" %} {% block content %} <p>Topics</p> <ul> ... </ul> <a href="{% url 'learning_logs:new_topic' %}">Add a new topic:</a> {% endblock content %} ``` ---- Мы должны создать форму, связанную с моделью Entry, но более специализированную по сравнению с TopicForm: ``` from django import forms from .models import Topic, Entry class TopicForm(forms.ModelForm): ... class EntryForm(forms.ModelForm): class Meta: model = Entry fields = ['text'] labels = {'text': 'Entry:'} widgets = {'text': forms.Textarea(attrs={'cols': 80})} ``` ---- Вот как выглядит параметрический URL, который мы добавляем в learning_logs/urls.py: ``` urlpatterns = [ ... path('new_entry/<int:topic_id>/', views.new_entry, name='new_entry'), ] ``` ---- Функция представления new_entry очень похожа на функцию добавления новой темы: ``` from django.shortcuts import render, redirect from .models import Topic from .forms import TopicForm, EntryForm ... def new_entry(request, topic_id): topic = Topic.objects.get(id=topic_id) if request.method != 'POST': form = EntryForm() else: form = EntryForm(data=request.POST) if form.is_valid(): new_entry = form.save(commit=False) new_entry.topic = topic new_entry.save() return redirect('learning_logs:topic', topic_id=topic_id) context = {'topic': topic, 'form': form} return render(request, 'learning_logs/new_entry.html', context) ``` ---- Как видно из следующего кода, шаблон new_entry похож на шаблон new_topic: ``` {% extends "learning_logs/base.html" %} {% block content %} <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p> <p>Add a new entry:</p> <form action="{% url 'learning_logs:new_entry' topic.id %}" method='post'> {% csrf_token %} {{ form.as_p }} <button name='submit'>add entry</button> </form> {% endblock content %} ``` ---- Затем необходимо создать ссылку на страницу new_entry на каждой странице темы: ``` {% extends "learning_logs/base.html" %} {% block content %} <p>Topic: {{ topic }}</p> <p>Entries:</p> <p> <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a> </p> <ul> ... </ul> {% endblock content %} ``` ---- В URL-адресе страницы должен передаваться идентификатор редактируемой записи: ``` urlpatterns = [ ... path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'), ] ``` ---- Создадим функцию для измениения таблицы entry: ``` from django.shortcuts import render, redirect from .models import Topic, Entry from .forms import TopicForm, EntryForm def edit_entry(request, entry_id): entry = Entry.objects.get(id=entry_id) topic = entry.topic if request.method != 'POST': form = EntryForm(instance=entry) else: form = EntryForm(instance=entry, data=request.POST) if form.is_valid(): form.save() return redirect('learning_logs:topic', topic_id=topic.id) context = {'entry': entry, 'topic': topic, 'form': form} return render(request, 'learning_logs/edit_entry.html', context) ``` ---- Шаблон edit_entry.html очень похож на new_entry.html: ``` {% extends "learning_logs/base.html" %} {% block content %} <p><a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a></p> <p>Edit entry:</p> <form action="{% url 'learning_logs:edit_entry' entry.id %}" method='post'> {% csrf_token %} {{ form.as_p }} <button name="submit">save changes</button> </form> {% endblock content %} ``` ---- Теперь необходимо включить ссылку на страницу edit_entry в каждую тему на странице со списком тем: ``` {% for entry in entries %} <li> <p>{{ entry.date_added|date:'M d, Y H:i' }}</p> <p>{{ entry.text|linebreaks }}</p> <p> <a href="{% url 'learning_logs:edit_entry' entry.id %}">Edit entry</a> </p> </li> ``` --- ## Приложение пользователя ---- Начнем с создания нового приложения users командой startapp: `python manage.py startapp users` ---- Новое приложение необходимо добавить в settings.py: ``` INSTALLED_APPS = [ ... 'learning_logs', 'users', ] ``` ---- Затем необходимо изменить корневой файл urls.py, чтобы он включал URL-адреса, написанные для приложения users: ``` from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('users/', include('users.urls')), path('', include('learning_logs.urls')), ] ``` ---- Создайте новый файл urls.py в каталоге learning_log/users/ и добавьте в него следующий код: ``` from django.urls import path, include app_name = 'users' urlpatterns = [ path('', include('django.contrib.auth.urls')), ] ``` ---- Вот как выглядит шаблон login.html, который должен находиться в learning_log/users/templates/registration/: ``` {% extends "learning_logs/base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} <form method="post" action="{% url 'users:login' %}"> {% csrf_token %} {{ form.as_p }} <button name="submit">log in</button> <input type="hidden" name="next" value="{% url 'learning_logs:index' %}"> </form> {% endblock content %} ``` ---- Добавим ссылку на страницу входа в base.html, чтобы она присутствовала на каждой странице. Ссылка не должна отображаться, если пользователь уже прошел процедуру входа, поэтому она вкладывается в тег {% if %}: ``` <p> <a href="{% url 'learning_logs:index' %}">Learning Log</a> - <a href="{% url 'learning_logs:topics' %}">Topics</a> - {% if user.is_authenticated %} Hello, {{ user.username }}. {% else %} <a href="{% url 'users:login' %}">log in</a> {% endif %} </p> {% block content %}{% endblock content %} ``` ---- Теперь нужно создать ссылку для выхода: ``` {% if user.is_authenticated %} Hello, {{ user.username }}. <a href="{% url 'users:logout' %}">log out</a> {% else %} ``` ---- Сохраните файл logged_out.html в каталоге templates/registration — в том же каталоге, в котором был сохранен файл login.html: ``` {% extends "learning_logs/base.html" %} {% block content %} <p>You have been logged out. Thank you for visiting!</p> {% endblock content %} ``` ---- Следующий код предоставляет шаблон URL для страницы регистрации — также в файле users/urls.py: ``` from django.urls import path, include from . import views app_name = 'users' urlpatterns = [ path('', include('django.contrib.auth.urls')), path('register/', views.register, name='register'), ] ``` ---- Функция представления register() должна вывести пустую форму регистрации при первом запросе страницы регистрации: ``` from django.shortcuts import render, redirect from django.contrib.auth import login from django.contrib.auth.forms import UserCreationForm def register(request): if request.method != 'POST': form = UserCreationForm() else: form = UserCreationForm(data=request.POST) if form.is_valid(): new_user = form.save() login(request, new_user) return redirect('learning_logs:index') context = {'form': form} return render(request, 'users/register.html', context) ``` ---- Шаблон страницы регистрации похож на шаблон страницы входа. Проследите за тем, чтобы он был сохранен в одном каталоге с login.html: ``` {% extends "learning_logs/base.html" %} {% block content %} <form method="post" action="{% url 'users:register' %}"> {% csrf_token %} {{ form.as_p }} <button name="submit">register</button> <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" /> </form> {% endblock content %} ``` ---- Следующий шаг — добавление кода для вывода ссылки на страницу регистрации для любого пользователя, еще не выполнившего вход: ``` {% if user.is_authenticated %} Hello, {{ user.username }}. <a href="{% url 'users:logout' %}">log out</a> {% else %} <a href="{% url 'users:register' %}">Register</a> - <a href="{% url 'users:login' %}">log in</a> {% endif %} ``` ---- Такая система регистрации позволяет любому пользователю создать сколько угодно учетных записей Learning Log. Однако некоторые системы требуют, чтобы пользователь подтвердил свою заявку, отправляя сообщение электронной почты, на которое пользователь должен ответить. ---- При таком подходе в системе будет создано меньше спамерских учетных записей, чем в простейшей системе из нашего примера. Но пока вы только учитесь строить приложения, вполне нормально тренироваться на упрощенной системе регистрации вроде используемой нами. --- ## Домашнее задание ---- Блог: создайте новый проект Django с именем Blog. Создайте в проекте приложение с именем blogs и моделью BlogPost. Модель должна содержать такие поля, как title, text и date_added. Создайте суперпользователя для проекта и воспользуйтесь административным сайтом для создания пары коротких сообщений. Создайте домашнюю страницу, на которой выводятся все сообщения в хронологическом порядке. ---- Сделайте одну форму для создания новых сообщений, а другую форму для редактирования существующих сообщений. Заполните формы и убедитесь в том, что они работают. ---- Учетные записи в блоге: добавьте систему аутентификации и регистрации в проект Blog, работа над которым началась в упражнении 1. Проследите за тем, чтобы пользователь, выполнивший вход, видел свое имя где-то на экране, а незарегистрированные пользователи видели ссылку на страницу регистрации.
{"metaMigratedAt":"2023-06-16T13:02:18.927Z","metaMigratedFrom":"Content","title":"Формы и приложение пользователей","breaks":true,"contributors":"[{\"id\":\"0d39d5a3-691d-488c-8f1e-1a0fb0be4f13\",\"add\":11036,\"del\":32}]"}
    321 views