vault backup: 2026-05-31 10:23:48
This commit is contained in:
Vendored
+15
-16
File diff suppressed because one or more lines are too long
@@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
status: seed
|
status: processing
|
||||||
type: concept
|
type: concept
|
||||||
tags:
|
tags:
|
||||||
- django
|
- django
|
||||||
@@ -9,79 +9,182 @@ tags:
|
|||||||
- modelform
|
- modelform
|
||||||
created: 2026-05-30
|
created: 2026-05-30
|
||||||
updated: 2026-05-31
|
updated: 2026-05-31
|
||||||
|
title: "Django: Работа с формами и CUD"
|
||||||
source:
|
source:
|
||||||
- '[[lec5-django_cud_form.pdf]]'
|
- '[[lec5-django_cud_form.pdf]]'
|
||||||
aliases:
|
aliases:
|
||||||
- 'Django: Формы и CUD'
|
- 'Django: Формы и CUD'
|
||||||
---
|
---
|
||||||
# Django: Формы и CUD
|
|
||||||
|
|
||||||
**CUD (Create, Update, Delete)** — операции изменения данных. Django предоставляет мощную подсистему форм для их реализации.
|
# Django: Работа с формами и CUD
|
||||||
|
|
||||||
|
**CUD (Create, Update, Delete)** — операции изменения данных: создать, обновить, удалить.
|
||||||
|
|
||||||
|
Форма в Django — это слой между HTML-формой и Python-кодом. Она принимает данные от пользователя, проверяет их, приводит к нормальным типам и помогает сохранить результат.
|
||||||
|
|
||||||
## Типы форм
|
## Типы форм
|
||||||
1. **`forms.Form`**: Для общих задач (поиск, обратная связь), не привязанных к моделям.
|
- **`forms.Form`** — обычная форма, не привязанная к модели. Подходит для поиска, обратной связи, фильтров, отправки email.
|
||||||
2. **`forms.ModelForm`**: Автоматическая генерация полей на основе модели.
|
- **`forms.ModelForm`** — форма на основе модели. Django сам создаёт поля формы по полям модели и умеет сохранять объект через `form.save()`.
|
||||||
|
|
||||||
|
Если данные не нужно сохранять в таблицу напрямую — чаще подходит `forms.Form`. Если форма создаёт или редактирует модель — проще использовать `forms.ModelForm`.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
from .models import Blog
|
||||||
|
|
||||||
|
|
||||||
class BlogForm(forms.ModelForm):
|
class BlogForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Blog
|
model = Blog
|
||||||
fields = ['name', 'tagline']
|
fields = ["name", "tagline"]
|
||||||
widgets = {
|
widgets = {
|
||||||
'name': forms.TextInput(attrs={'class': 'form-control'})
|
"name": forms.TextInput(attrs={"class": "form-control"})
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Жизненный цикл формы в представлении
|
## Жизненный цикл формы в представлении
|
||||||
|
Обычно view работает так:
|
||||||
|
1. При `GET` создаёт пустую форму и показывает страницу.
|
||||||
|
2. При `POST` создаёт форму из `request.POST`.
|
||||||
|
3. Проверяет данные через `is_valid()`.
|
||||||
|
4. Если данные корректные — выполняет действие и делает `redirect`.
|
||||||
|
5. Если есть ошибки — снова показывает форму с ошибками.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
from django.shortcuts import redirect, render
|
||||||
|
|
||||||
|
|
||||||
def contact_view(request):
|
def contact_view(request):
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
form = ContactForm(request.POST)
|
form = ContactForm(request.POST)
|
||||||
|
|
||||||
if form.is_valid():
|
if form.is_valid():
|
||||||
# Данные очищены и доступны в form.cleaned_data
|
|
||||||
data = form.cleaned_data
|
data = form.cleaned_data
|
||||||
# Действие (сохранение или отправка email)
|
send_email(data["email"], data["message"])
|
||||||
return redirect('success')
|
return redirect("success")
|
||||||
else:
|
else:
|
||||||
form = ContactForm()
|
form = ContactForm()
|
||||||
return render(request, 'contact.html', {'form': form})
|
|
||||||
|
return render(request, "contact.html", {"form": form})
|
||||||
```
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Валидация и очистка данных
|
## Валидация и очистка данных
|
||||||
- **`is_valid()`**: Запускает процесс проверки.
|
- **`is_valid()`** — запускает проверку формы. После него появляются `form.cleaned_data` и `form.errors`.
|
||||||
- **Автоматическая очистка**: Превращает текст в типы Python (например, строку в `datetime`).
|
- **Автоматическая очистка** — Django превращает строки из HTML-формы в Python-типы: число в `int`, дату в `date`, email в строку после проверки формата.
|
||||||
- **`clean_<fieldname>()`**: Валидация конкретного поля.
|
- **`clean_<fieldname>()`** — проверка одного конкретного поля.
|
||||||
- **`clean()`**: Валидация всей формы (сравнение полей).
|
- **`clean()`** — проверка всей формы, когда нужно сравнить несколько полей между собой.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from django import forms
|
||||||
|
|
||||||
|
|
||||||
|
class RegisterForm(forms.Form):
|
||||||
|
username = forms.CharField(max_length=50)
|
||||||
|
password = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
password_repeat = forms.CharField(widget=forms.PasswordInput)
|
||||||
|
|
||||||
|
def clean_username(self):
|
||||||
|
username = self.cleaned_data["username"]
|
||||||
|
|
||||||
|
if username.lower() == "admin":
|
||||||
|
raise forms.ValidationError("Это имя нельзя использовать")
|
||||||
|
|
||||||
|
return username
|
||||||
|
|
||||||
|
def clean(self):
|
||||||
|
cleaned_data = super().clean()
|
||||||
|
password = cleaned_data.get("password")
|
||||||
|
password_repeat = cleaned_data.get("password_repeat")
|
||||||
|
|
||||||
|
if password and password_repeat and password != password_repeat:
|
||||||
|
raise forms.ValidationError("Пароли не совпадают")
|
||||||
|
|
||||||
|
return cleaned_data
|
||||||
|
```
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Generic Editing Views (CBV для CUD)
|
## Generic Editing Views (CBV для CUD)
|
||||||
Django предоставляет готовые классы для CRUD:
|
Django предоставляет готовые классы для типовых страниц редактирования:
|
||||||
- **CreateView**: Создание объекта.
|
- **`CreateView`** — создать объект.
|
||||||
- **UpdateView**: Редактирование (требует `pk` в URL).
|
- **`UpdateView`** — изменить объект; обычно ищет его по `pk` из URL.
|
||||||
- **DeleteView**: Удаление (требует подтверждения через POST).
|
- **`DeleteView`** — удалить объект; требует подтверждения через `POST`.
|
||||||
|
|
||||||
|
Главная идея: вместо ручной обработки `GET`, `POST`, `is_valid()` и `save()` можно описать модель, форму, шаблон и URL после успеха.
|
||||||
|
|
||||||
**Пример:**
|
|
||||||
```python
|
```python
|
||||||
|
from django.urls import reverse_lazy
|
||||||
|
from django.views.generic import CreateView, DeleteView, UpdateView
|
||||||
|
|
||||||
|
from .forms import BlogForm
|
||||||
|
from .models import Blog
|
||||||
|
|
||||||
|
|
||||||
class BlogCreateView(CreateView):
|
class BlogCreateView(CreateView):
|
||||||
model = Blog
|
model = Blog
|
||||||
form_class = BlogForm
|
form_class = BlogForm
|
||||||
template_name = "blog/blog_form.html"
|
template_name = "blog/blog_form.html"
|
||||||
success_url = reverse_lazy('blog_list')
|
success_url = reverse_lazy("blog_list")
|
||||||
|
|
||||||
|
|
||||||
|
class BlogUpdateView(UpdateView):
|
||||||
|
model = Blog
|
||||||
|
form_class = BlogForm
|
||||||
|
template_name = "blog/blog_form.html"
|
||||||
|
success_url = reverse_lazy("blog_list")
|
||||||
|
|
||||||
|
|
||||||
|
class BlogDeleteView(DeleteView):
|
||||||
|
model = Blog
|
||||||
|
template_name = "blog/blog_confirm_delete.html"
|
||||||
|
success_url = reverse_lazy("blog_list")
|
||||||
```
|
```
|
||||||
|
|
||||||
## Отображение в шаблоне
|
## Отображение в шаблоне
|
||||||
- `{{ form.as_p }}` — в параграфах.
|
- `{{ form.as_p }}` — быстро вывести поля формы в `<p>`.
|
||||||
- `{{ form.as_table }}` — в строках таблицы.
|
- `{{ form.as_table }}` — вывести поля как строки таблицы.
|
||||||
- **Ручное управление:**
|
- Ручное управление нужно, когда важна HTML-разметка, CSS-классы или отдельный вывод ошибок.
|
||||||
|
|
||||||
```html
|
```html
|
||||||
{% for field in form %}
|
<form method="post">
|
||||||
{{ field.label_tag }} {{ field }}
|
{% csrf_token %}
|
||||||
{{ field.errors }}
|
{{ form.as_p }}
|
||||||
{% endfor %}
|
<button type="submit">Сохранить</button>
|
||||||
|
</form>
|
||||||
```
|
```
|
||||||
*Важно:* Не забывайте про `{% csrf_token %}` внутри тега `<form>`.
|
|
||||||
|
```html
|
||||||
|
<form method="post">
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
{% for field in form %}
|
||||||
|
<div>
|
||||||
|
{{ field.label_tag }}
|
||||||
|
{{ field }}
|
||||||
|
{{ field.errors }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<button type="submit">Сохранить</button>
|
||||||
|
</form>
|
||||||
|
```
|
||||||
|
|
||||||
|
### CSRF-токен
|
||||||
|
**CSRF (Cross-Site Request Forgery)** — атака, при которой чужой сайт пытается отправить запрос от имени уже авторизованного пользователя.
|
||||||
|
|
||||||
|
Пример: пользователь залогинен в Django-приложении. Он открывает вредную страницу, а она незаметно отправляет `POST`-запрос на удаление объекта. Без защиты браузер приложит cookies пользователя, и сервер может принять запрос как настоящий.
|
||||||
|
|
||||||
|
`{% csrf_token %}` добавляет в форму скрытое поле с защитным токеном:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<input type="hidden" name="csrfmiddlewaretoken" value="...">
|
||||||
|
```
|
||||||
|
|
||||||
|
При `POST` Django сверяет токен из формы с токеном пользователя. Если токена нет или он неправильный, запрос отклоняется.
|
||||||
|
|
||||||
|
Короткое правило: во все HTML-формы с `method="post"` добавлять `{% csrf_token %}`.
|
||||||
|
|||||||
+1
-2
@@ -523,7 +523,7 @@ total: 467
|
|||||||
|---------|---------|--------|------|------|
|
|---------|---------|--------|------|------|
|
||||||
| [[Django: Модели и ORM]] | Django Модели и ORM | processing | concept | [django, backend, orm, database, migrations] |
|
| [[Django: Модели и ORM]] | Django Модели и ORM | processing | concept | [django, backend, orm, database, migrations] |
|
||||||
| [[Django: Представления, шаблоны и CBV]] | Django: Представления, шаблоны и CBV | processing | concept | [django, routing, templates, cbv, fbv] |
|
| [[Django: Представления, шаблоны и CBV]] | Django: Представления, шаблоны и CBV | processing | concept | [django, routing, templates, cbv, fbv] |
|
||||||
| [[Django: Работа с формами и CUD]] | Django: Формы и CUD | seed | concept | [django, forms, crud, validation, modelform] |
|
| [[Django: Работа с формами и CUD]] | Django: Формы и CUD | processing | concept | [django, forms, crud, validation, modelform] |
|
||||||
| [[Python - MOC]] | Python - MOC | seed | moc | [programming, python] |
|
| [[Python - MOC]] | Python - MOC | seed | moc | [programming, python] |
|
||||||
| [[Python: Базовый синтаксис и основы]] | Основы Python | seed | concept | [python, syntax, programming-basics, functional-programming] |
|
| [[Python: Базовый синтаксис и основы]] | Основы Python | seed | concept | [python, syntax, programming-basics, functional-programming] |
|
||||||
| [[Курс по Python и Django]] | Курс по Python и Django | seed | moc | [python, django, backend] |
|
| [[Курс по Python и Django]] | Курс по Python и Django | seed | moc | [python, django, backend] |
|
||||||
@@ -767,4 +767,3 @@ total: 467
|
|||||||
| [[Daily Note]] | — | — | daily | [daily] |
|
| [[Daily Note]] | — | — | daily | [daily] |
|
||||||
| [[Study Item]] | — | seed | knowledge | [] |
|
| [[Study Item]] | — | seed | knowledge | [] |
|
||||||
| [[Travel Item]] | — | seed | knowledge | [travel] |
|
| [[Travel Item]] | — | seed | knowledge | [travel] |
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user