Skip to content

Latest commit

 

History

History
368 lines (352 loc) · 13.6 KB

File metadata and controls

368 lines (352 loc) · 13.6 KB

Django Templates & Forms

HTML

<html>
  <head>
    <title>Мир! Труд! Май!</title>
  </head>
  <body>
    <center
      onclick="alert('привет!')"
      style="color:red;font-size:40px"
    >
      <!-- тут нужно вставить своё имя -->
      <i>Привет, &lt;ИМЯ&gt;!</i>
    </center>
  </body>
</html>

Django templates

<html>
  <head>
    <title>Мир! Труд! Май!</title>
  </head>
  <body>
    <center
      onclick="alert('привет!')"
      style="color:red;font-size:40px"
      >
      <i>Привет, {{ name }}!</i>
    </center>
  </body>
</html>

наследование

extends
взять за основу расширяемый шаблон и переопределить в нём нужные блоки. Остальное содержимое шаблона останется прежним.
include
вставить на место {% include … %} содержимое подключаемого шаблона.

передача переменных

  • В шаблоне доступны объекты из middleware, например user
  • Нужные вам переменные вы передаёте из вьюхи во время рендеринга

Дополнительная литература

HTML формы

*HTML-формы* это просто текст. И вообщем-то мы можем писать его вручную или, например, с использованием f-string в Python.

<form action="." method="post">
 <label for="message">Послание</label>
 <input type='text' name="message"
        value="сообщение"
        maxlength="100" />
 <input type="submit"
       value="Отправить" />
</form>

simple_html_form.png

HTML формы

Формы могут быть более красивыми

html_form1.jpg

HTML формы

И очень разнообразными. Но писать такие разнообразные формы вручную это очень утомительно. На помощь нам приходят *Django Forms*.

html_form.png

Django формы

from django import forms

class MessageForm(forms.Form):
    message = forms.CharField(
        label='Послание',
        max_length=100
    )

form = MessageForm(
    initial={'message': 'сообщение'}
)

initial заполнит форму какими-то данными. При рендеринге это будет значение атрибута value в HTML. Обычно мы передаём туда request.POST

Django формы

<tr>
  <th>
    <label for="id_message">
      Послание:
    </label>
  </th>
  <td>
    <input type="text"
           name="message"
           value="сообщение"
           maxlength="100"
           required
           id="id_message" />
  </td>
</tr>

Django формы

Поля формы в Django описываются классами Field, каждый из которых имеет своё представление в виде Widget-а. file:builtin_fields.png

Bound / Unbond forms

Формы в Django могут быть в двух состояних

unbound
— форма пустая
bound
— форма заполнена данными

Unbound

Форма не связана ни с какими данными

form = MessageForm()
form.is_bound   # -> False

Bound

Форма частично или полностью заполнена

# обычно мы передаём request.POST
form = MessageForm({
    'message': 'foobar'
})
form.is_bound   # -> True

Валидация форм

*Документация*

form.is_valid()  # -> True / False
# в случае когда is_valid -> True,
# тогда у формы появляется атрибут
# cleaned_data, который содержит
# словарь со значениями полей
form.cleaned_data['field_name']
# если is_valid -> False
# то заполняется переменная
form.errors

Валидаторы

Пример написания своего валидатора

from django.core.exceptions import (
    ValidationError
)

def validate_even(value):
    if value % 2 != 0:
        raise ValidationError(
            '%(value) нечётно',
            params={'value': value}
        )

Валидаторы

from django import forms

class EvenNumbersForm(forms.Form):
    number = forms.IntegerField(
        validators=[validate_even]
    )

validators добавит валидаторы к уже существующему базовому валидатору IntegerField

Валидаторы

*Готовых валидаторов очень много!*

validators.png

Forms Workflow

form_handling_-_standard.png

Рендеринг форм вручную

{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label
     for="{{form.subject.id_for_label}}" />
    >
      Email subject:
    </label>
    {{ form.subject }}
</div>

Безопасность

*Настоятельно рекомендую ознакомиться с этой документацией* \newline https://docs.djangoproject.com/en/3.2/topics/security/

CSRF

На сайте может быть обычная кнопка, предлагающая вам посмотреть фотографии.

csrf.png

CSRF

А на самом деле там будет отправляться форма перевода денег с вашего аккаунта на аккаунт злоумышленника.

<form
  action="bank.com/transfer.do"
  method="POST">
  <input type="hidden"
         name="acct" value="воришка"/>
  <input type="hidden"
         name="amount" value="$1kk"/>
  <input type="submit"
         value="View my pictures!"/>
</form>

CSRF

Но если на стороне банка используются csrf-токены в формах, то ничего страшного не случится. Запрос злоумышленника не может содержать нужное значение (случайное в рамках сессии) csrf-токена.

<form method="post"
      action="{% url signup %}">
  {% csrf_token %}
</form>

file:csrf_html.png

ModelForm

*Прекрасная документация*

ModelForm

У ModelForm появляется метод .save()

class NameForm(models.ModelForm):
    class Meta:
        model = Name

form = NameForm(request.POST)
# сохранить запись в базу данных
form.save()

ModelForm

save(commit=False)

class NameForm(models.ModelForm):
    class Meta:
        model = Name

form = NameForm(request.POST)
# создаёт объект модели Name
# но не записываем его в базу
model = form.save(commit=False)

ModelForm

class YaForm(models.ModelForm):
    class Meta:
        # содержит поля X, Y, Z
        model = YaModel
        fields = ['X', 'Y']

form = YaForm(request.POST)
# не передаст в модель Z,
# а значит в базу запишется
# пустое значение поля Z
from.save()

ModelForm

Один из вариантов решения — определить модель заранее

model = YaModel(Z='foobar')
form = YaForm(
    request.POST,
    instance=model
)
# форма будет содержать все
# поля заполненными
form.save()

ModelForm

Или использовать commit=False чтобы доопределить модель перед записью в БД.

form = YaForm(request.POST)
model = form.save(commit=False)
model.Z = 'foobar'
model.save()

ModelForm

Допустим, мы определили модель

class Article(models.Model):
  headline = models.CharField(
    max_length=200,
    null=True,
    blank=True,
  )
  content = models.TextField()

ModelForm

Если поле не перечислено в fields или добавлено в excludes в Meta-классе, то это поле будет исключено из данных передаваемых в модель.

class ArticleForm(ModelForm):
  slug = CharField(
    validators=[validate_slug]
  )

  class Meta:
    model = Article
    # slug не попадёт в save()
    fields = ['headline', 'content']

Widgets

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

file:widgets.png

Widgets

Можно добавлять стили и другие атрибуты виджетам

class CommentForm(forms.Form):
  name = forms.CharField(
    widget=forms.TextInput(
      attrs={'class': 'special'}
    )
  )
  url = forms.URLField()
  comment = forms.CharField(
    widget=forms.TextInput(
      attrs={'size': '40'}
    )
  )

Widgets

class CommentForm(ModelForm):
  class Meta:
    model = Comment
    fields = (
      'name', 'url', 'comment'
    )
    widgets = {
      'name': forms.TextInput(
        attrs={'class': 'special', 'rows': 20}
      ),
      'comment': forms.TextInput(
        attrs={'size': '40'}
      )
    }

Widgets

<input type="text" name="name"
       class="special" required>

<input type="url" name="url"
       required>

<input type="text" name="comment"
       size="40" required>

Немного практики

Вопросы-ответы

questions.jpg