Рассмотрим, как связь "один-ко многим" может быть реализована в 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), ]
В итоге получится следующий проект:
При запуске проекта и обращении к главной странице по умолчанию нет никаких товаров. И для этого перейдем к добавлению и добавим какой-нибудь товар:
Подобным образом можно выполнить редактирование товара: