Пример 1: Цепочка связей Category → Product → ProductDetail
Демонстрирует ForeignKey с PROTECT и OneToOne с CASCADE. При попытке удалить Category с продуктами — Django выбросит ProtectedError. Удаление Product автоматически удалит его ProductDetail.
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=40, unique=True)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
verbose_name_plural = 'categories'
class Supplier(models.Model):
name = models.CharField(max_length=100, unique=True)
contact_email = models.EmailField(unique=True)
phone_number = models.CharField(max_length=20, unique=True)
def __str__(self):
return self.name
class Product(models.Model):
name = models.CharField(max_length=100)
category = models.ForeignKey(
Category, on_delete=models.PROTECT, related_name='products'
)
supplier = models.ForeignKey(
Supplier, on_delete=models.PROTECT, related_name='products'
)
price = models.DecimalField(max_digits=10, decimal_places=2)
quantity = models.PositiveSmallIntegerField()
article = models.CharField(
max_length=100, unique=True,
help_text='Unique string product id',
db_index=True
)
available = models.BooleanField(default=True)
def __str__(self):
return self.name
class Meta:
ordering = ['category', 'quantity']
class ProductDetail(models.Model):
product = models.OneToOneField(
Product, on_delete=models.CASCADE, related_name='details'
)
description = models.TextField(null=True, blank=True)
manufacturing_date = models.DateField(null=True, blank=True)
expiration_date = models.DateField(null=True, blank=True)
weight = models.DecimalField(
null=True, blank=True, max_digits=5, decimal_places=2
)
def __str__(self):
return f"Details of {self.product.name}"
Пример 2: Customer с мягким удалением
Паттерн soft delete: запись не удаляется физически, а помечается флагом deleted=True. Поле deleted_at фиксирует момент удаления.
class Address(models.Model):
country = models.CharField(max_length=100)
city = models.CharField(max_length=100)
street = models.CharField(max_length=255)
house = models.CharField(max_length=6)
def __str__(self):
return f"{self.street}, {self.house}"
class Meta:
verbose_name_plural = 'addresses'
class Customer(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
phone_number = models.CharField(max_length=15)
address = models.OneToOneField(
Address, null=True, on_delete=models.SET_NULL, related_name='customer'
)
date_joined = models.DateTimeField(auto_now_add=True)
deleted = models.BooleanField(default=False)
deleted_at = models.DateTimeField(null=True, blank=True)
def __str__(self):
return f"{self.first_name} {self.last_name}"
class Meta:
ordering = ['-date_joined']
get_latest_by = 'date_joined'
Пример 3: Admin с list_editable и поиском по связанной модели
from django.contrib import admin
from .models import Category, Supplier, Product, Customer, Order
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ('name',)
search_fields = ('name',)
ordering = ('name',)
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'category', 'supplier', 'price', 'quantity', 'article', 'available']
list_filter = ['category', 'supplier', 'available']
search_fields = ['name', 'article']
ordering = ['category', 'quantity']
list_editable = ['price', 'quantity', 'available']
@admin.register(Customer)
class CustomerAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email', 'phone_number', 'date_joined', 'deleted')
search_fields = ('first_name', 'last_name', 'email', 'phone_number')
ordering = ('-date_joined',)
list_filter = ('deleted',)
list_editable = ('deleted',)
@admin.register(Order)
class OrderAdmin(admin.ModelAdmin):
list_display = ('id', 'order_date', 'customer')
# Поиск по полям связанной модели Customer через __
search_fields = ('customer__first_name', 'customer__last_name', 'customer__email')
ordering = ('-order_date',)
Пример 4: Доступ через related_name
После создания связей с related_name можно обращаться «в обратную сторону».
# После создания объектов в shell:
# python manage.py shell
from myapp.models import Category, Product, Customer, Order
electronics = Category.objects.get(name='Electronics')
# Все товары из категории
products = electronics.products.all()
# Детали продукта через OneToOne
product = Product.objects.first()
details = product.details # ProductDetail
# Все заказы покупателя
customer = Customer.objects.first()
orders = customer.orders.all()
# Последний заказ (благодаря get_latest_by)
latest = Order.objects.latest()