Рассмотрим, как связь "один-ко многим" может быть реализована в Django на простейшем примере. Пусть в файле models.py приложения определены следующие модели:
from django.db import models
class Company(models.Model):
name = models.CharField(max_length=30)
class Product(models.Model):
company = models.ForeignKey(Company, on_delete = models.CASCADE)
name = models.CharField(max_length=30)
price = models.IntegerField()
здесь определены модели Company и Product, которые связаны связью "один-ко-многим": одна компания может иметь множество товаров.
В файле views.py определим все необходимые представления для работы с объектами Product:
from django.shortcuts import render
from .models import Company, Product
from django.http import HttpResponseRedirect, HttpResponseNotFound
# получение данных из бд
def index(request):
products = Product.objects.all()
return render(request, "index.html", {"products": products})
# добавление данных из бд
def create(request):
create_companies() # добавляем начальные данные для компаний
# если запрос POST, сохраняем данные
if request.method == "POST":
product = Product()
product.name = request.POST.get("name")
product.price = request.POST.get("price")
product.company_id = request.POST.get("company")
product.save()
return HttpResponseRedirect("/")
# передаем данные в шаблон
companies = Company.objects.all()
return render(request, "create.html", {"companies": companies})
# изменение данных в бд
def edit(request, id):
try:
product = Product.objects.get(id=id)
if request.method == "POST":
product.name = request.POST.get("name")
product.price = request.POST.get("price")
product.company_id = request.POST.get("company")
product.save()
return HttpResponseRedirect("/")
else:
companies = Company.objects.all()
return render(request, "edit.html", {"product": product, "companies": companies})
except Product.DoesNotExist:
return HttpResponseNotFound("<h2>Product not found</h2>")
# удаление данных из бд
def delete(request, id):
try:
product = Product.objects.get(id=id)
product.delete()
return HttpResponseRedirect("/")
except Product.DoesNotExist:
return HttpResponseNotFound("<h2>Product not found</h2>")
# добавление начальных данных в таблицу компаний
def create_companies():
if Company.objects.all().count() == 0:
Company.objects.create(name = "Apple")
Company.objects.create(name = "Asus")
Company.objects.create(name = "MSI")
Для упрошения добавления данных здесь также определена вспомогательная функция create_companies, которая добавляет данные компаний в бд, чтобы у нас были некоторые начальные данные.
Функция index получает из базы данных все объекты Product и передает их в шаблон.
Функция create, если запрос типа POST, то получает данные из запроса и сохраняет их в бд. Иначе получает набор компаний и передает их в шаблон для добавления.
Функция edit, если запрос типа POST, получает данные запроса и изменяет значения нужного товара. Если запрос типа Get, то передает в шаблон данные редактируемого товара и список компаний (чтобы можно было изменить компанию товара - представим, что мы можем изменить производителя товара)
Функция delete удаляет товар.
В каталоге приложения определим папку templates и добавим в нее три шаблона: index.html, create.html и edit.html
В шаблоне index.html определим логику вывода списка товаров:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>METANIT.COM</title>
</head>
<body>
<h2>Список товаров</h2>
<a href="create/">Добавление товара</a>
<table>
<thead><th>Название</th><th>Цена</th><th>Компания</th><th></th></thead>
{% for product in products %}
<tr>
<td>{{ product.name }}</td>
<td>{{ product.price }}</td>
<td>{{ product.company.name }}</td>
<td>
<a href="edit/{{product.id}}">Изменить</a> | <a href="delete/{{product.id}}">Удалить</a>
</td>
</tr>
{% endfor %}
</table>
</body>
</html>
В шаблоне create.html определим поля для добавления товара:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>METANIT.COM</title>
</head>
<body>
<h2>Добавление товара</h2>
<form method="POST">
{% csrf_token %}
<p>
<label>Введите название товара</label><br>
<input type="text" name="name" />
</p>
<p>
<label>Введите цену</label><br>
<input type="number" name="price" />
</p>
<p>
<label>Введите компанию</label><br>
<select name="company">
{% for company in companies %}
<option value="{{company.id}}">{{company.name}}</option>
{% endfor %}
</select>
</p>
<input type="submit" value="Сохранить" >
</form>
</body>
</html>
В шаблоне edit.html определим поля для редактирования товара:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>METANIT.COM</title>
</head>
<body>
<h2>Изменение товара</h2>
<form method="POST">
{% csrf_token %}
<p>
<label>Введите название товара</label><br>
<input type="text" name="name" value="{{product.name}}" />
</p>
<p>
<label>Введите цену</label><br>
<input type="number" name="price" value="{{product.price}}"/>
</p>
<p>
<label>Введите компанию</label><br>
<select name="company">
{% for company in companies %}
<option value="{{company.id}}"
{%if company.id == product.company_id%}
selected
{%endif%}
>{{company.name}}</option>
{% endfor %}
</select>
</p>
<input type="submit" value="Сохранить" >
</form>
</body>
</html>
В файле urls.py пропишем маршруты для представлений:
from django.urls import path
from hello import views
urlpatterns = [
path("", views.index),
path("create/", views.create),
path("edit/<int:id>/", views.edit),
path("delete/<int:id>/", views.delete),
]
В итоге получится следующий проект:
При запуске проекта и обращении к главной странице по умолчанию нет никаких товаров. И для этого перейдем к добавлению и добавим какой-нибудь товар:
Подобным образом можно выполнить редактирование товара: