💻 Примеры: Модели Django

⚡ Минимальный пример

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200, verbose_name='Название')
    author = models.CharField(max_length=100)
    published_date = models.DateField(null=True, blank=True)
    in_stock = models.BooleanField(default=True)

    def __str__(self):
        return self.title

    class Meta:
        ordering = ['-published_date']

Пример 1: Базовая модель из лекции

Самая простая модель — основа всего. Точно как в лекции:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=200)   # VARCHAR(200)
    author = models.CharField(max_length=100)  # VARCHAR(100)
    published_date = models.DateField()        # DATE

# Django автоматически добавит: id BIGINT PRIMARY KEY AUTOINCREMENT

SQL, который сгенерирует Django при migrate:

CREATE TABLE "myapp_book" (
    "id" serial NOT NULL PRIMARY KEY,
    "title" varchar(200) NOT NULL,
    "author" varchar(100) NOT NULL,
    "published_date" date NOT NULL
);

Пример 2: Все строковые поля

from django.db import models

class Article(models.Model):
    # CharField — короткая строка, обязательно max_length
    title = models.CharField(max_length=200, verbose_name='Заголовок')

    # TextField — длинный текст, TEXT в БД
    body = models.TextField(verbose_name='Текст статьи')

    # EmailField — валидирует формат email
    author_email = models.EmailField(verbose_name='Email автора')

    # SlugField — URL-метка (только буквы, цифры, -_)
    slug = models.SlugField(unique=True, verbose_name='Слаг')

    # URLField — валидирует формат URL
    source_url = models.URLField(blank=True, verbose_name='Источник')

Пример 3: Числовые поля

from django.db import models

class Product(models.Model):
    name = models.CharField(max_length=200)

    # IntegerField — целое число
    quantity = models.IntegerField(default=0)

    # PositiveIntegerField — только >= 0
    views_count = models.PositiveIntegerField(default=0)

    # DecimalField — денежные значения, фиксированная точность
    # max_digits=10: всего 10 цифр; decimal_places=2: 2 после запятой
    price = models.DecimalField(max_digits=10, decimal_places=2, default=0.00)

    # FloatField — приближённое число (не для денег!)
    rating = models.FloatField(default=0.0)

    def __str__(self):
        return f"{self.name} — {self.price} руб."

Пример 4: Поля дата/время с auto_now

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)

    # DateField — только дата
    publish_date = models.DateField(null=True, blank=True)

    # DateTimeField с auto_now_add — автоматически при создании объекта
    # editable=False — нельзя менять в Admin
    created_at = models.DateTimeField(auto_now_add=True)

    # DateTimeField с auto_now — обновляется при каждом save()
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title
auto_now vs auto_now_add:
  • auto_now_add=True — устанавливается один раз при создании объекта. Не меняется.
  • auto_now=True — обновляется при каждом вызове save(). Подходит для «дата изменения».
Оба параметра делают поле нередактируемым в Admin (editable=False автоматически).

Пример 5: Поле choices с TextChoices

from django.db import models

class Task(models.Model):
    # TextChoices — современный способ задать choices (Django 3.0+)
    class Status(models.TextChoices):
        NEW = 'new', 'Новая'
        IN_PROGRESS = 'in_progress', 'В работе'
        PENDING = 'pending', 'Ожидание'
        BLOCKED = 'blocked', 'Заблокирована'
        DONE = 'done', 'Выполнена'

    title = models.CharField(max_length=200)
    status = models.CharField(
        max_length=20,
        choices=Status.choices,
        default=Status.NEW,
        verbose_name='Статус'
    )

    def __str__(self):
        return f"{self.title} [{self.get_status_display()}]"
        # get_FOO_display() — возвращает читаемое название статуса

Пример 6: ForeignKey — многие к одному

from django.db import models

class Author(models.Model):
    name = models.CharField(max_length=100)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=200)

    # ForeignKey — on_delete ОБЯЗАТЕЛЕН в Django 5.x
    # CASCADE — при удалении автора все его книги тоже удаляются
    author = models.ForeignKey(
        Author,
        on_delete=models.CASCADE,
        related_name='books',    # author.books.all() — все книги автора
        verbose_name='Автор'
    )

    def __str__(self):
        return f"{self.title} — {self.author.name}"


# Использование в shell:
# author = Author.objects.get(name="Пушкин")
# books = author.books.all()   # через related_name
# book = Book.objects.get(id=1)
# print(book.author.name)      # доступ к связанному объекту

Пример 7: ManyToManyField — многие ко многим

from django.db import models

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name


class Article(models.Model):
    title = models.CharField(max_length=200)

    # ManyToManyField — Django сам создаёт промежуточную таблицу
    tags = models.ManyToManyField(
        Tag,
        blank=True,              # в форме можно не указывать теги
        related_name='articles', # tag.articles.all()
        verbose_name='Теги'
    )

    def __str__(self):
        return self.title


# Использование в shell:
# article = Article.objects.get(id=1)
# article.tags.add(tag1, tag2)   # добавить теги
# article.tags.all()              # все теги статьи
# article.tags.remove(tag1)       # убрать тег

Пример 8: OneToOneField — один к одному

from django.db import models

class User(models.Model):
    username = models.CharField(max_length=150, unique=True)
    email = models.EmailField(unique=True)

    def __str__(self):
        return self.username


class UserProfile(models.Model):
    # OneToOneField — расширение модели User (доп. данные)
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name='profile',   # user.profile — доступ к профилю
        verbose_name='Пользователь'
    )
    bio = models.TextField(blank=True, verbose_name='О себе')
    avatar = models.ImageField(
        upload_to='avatars/',
        null=True,
        blank=True,
        verbose_name='Аватар'
    )

    def __str__(self):
        return f"Профиль: {self.user.username}"


# Использование в shell:
# user = User.objects.get(username='ivan')
# profile = user.profile    # через related_name
# profile.bio = 'Python developer'
# profile.save()

Пример 9: Параметры полей — null, blank, default, validators

from django.db import models
from django.core.validators import MinLengthValidator, MinValueValidator


class Product(models.Model):
    # null=True — разрешает NULL в БД (для строк лучше blank=True)
    description = models.TextField(null=True, blank=True)

    # blank=True — разрешает пустое значение в форме
    subtitle = models.CharField(max_length=200, blank=True, default='')

    # unique=True — значение должно быть уникальным
    sku = models.CharField(max_length=50, unique=True)

    # validators — проверка значения
    price = models.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0.01)],    # цена > 0
        verbose_name='Цена'
    )

    # help_text — подсказка в Admin
    code = models.CharField(
        max_length=10,
        validators=[MinLengthValidator(3)],
        help_text='Код продукта: 3–10 символов',
        verbose_name='Код'
    )

    # error_messages — кастомные сообщения об ошибках
    name = models.CharField(
        max_length=200,
        error_messages={
            'blank': 'Название не может быть пустым.',
            'unique': 'Продукт с таким названием уже существует.'
        },
        verbose_name='Название'
    )

Пример 10: Регистрация в Admin

# myapp/admin.py
from django.contrib import admin
from .models import Book, Author

# Вариант 1: простая регистрация
admin.site.register(Book)

# Вариант 2: декоратор + кастомный AdminModel
@admin.register(Author)
class AuthorAdmin(admin.ModelAdmin):
    list_display = ('name', 'email')      # столбцы в списке объектов
    search_fields = ('name', 'email')     # поиск по этим полям
    ordering = ('name',)                  # сортировка в списке