Страницы

Поиск по вопросам

среда, 10 июля 2019 г.

ContentTypes. Обобщённые связи? Реализация отношений моделей

Предположим есть <Человек>. У него может быть только один(один_к_одному) транспорт. Транспортом может быть <Автомобиль>, <Самолёт>, <Лошадь>, то есть эти три класса совершенно разные.
Как реализовать модели? Смотрю в сторону ContentTypes. Читаю доку но не всё понятно пока, и поэтому я не совсем уверен в выборе направления.
Если contenttypes всё-таки правильный выбор, то можно пример для толчка, на основе задачи выше?


Ответ

ContentTypes позволяют действительно ссылаться на экземпляры разных моделей. Если вы хотите решить вашу задачу через них, то вы должны создать поля как описано в документации
from django.db import models from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType
class Car(models.Model): pass
class Plane(models.Model): pass
class Horse(models.Model): pass
class Human(models.Model): content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() transport = GenericForeignKey('content_type', 'object_id')
Ну и в дальнейшем можно присваивать экземплярам модели Human в качестве transport экземпляры любых моделей, зарегистрированных в django.
Но я бы на вашем месте обошёлся без них. Можно создать класс транспорт, а от него отнаследовать автомобиль, самолёт, лошадь:
from django.db import models
class Transport(models.Model): pass
class Car(Transport): pass
class Plane(Transport): pass
class Horse(Transport): pass
class Human(models.Model): transport = models.ForeignKey(Transport)
UPD:
Для примера доработаем наши модели следующим образом:
class Transport(models.Model): max_speed = models.IntegerField('Максимальная скорость') capacity = models.IntegerField('Вместимость')
class Car(Transport): doors = models.IntegerField('Количество дверей')
class Plane(Transport): wing_size = models.IntegerField('Размах крыла')
class Horse(Transport): date_birth = models.DateTimeField('Дата рождения')
class Human(models.Model): transport = models.OneToOneField(Transport)
То есть теперь у нас есть различные атрибуты в разных моделях. Все их перечислять не буду, они хорошо читаются в коде. Нужно лишь отметить, что у нас теперь есть общая модель Transport со специфическими для всех транспортных средств полями и наследуемые от неё типы транспорта, которые определяют свои атрибуты.
Как этим пользоваться? Давайте рассмотрим всё на примерах
>>> # Создадим по одному экземпляру каждой модели - авто, самолёта и лошади >>> Car.objects.create(doors=5, max_speed=130, capacity=5) >>> Plane.objects.create(wing_size=30, max_speed=1100, capacity=80) >>> import datetime >>> Horse.objects.create(date_birth=datetime.datetime.now() - datetime.timedelta(days=365), max_speed=10, capacity=1) >>> # теперь создадим 3 челокека, к которым сразу же привяжем транспорт >>> Human.objects.create(transport=Car.objects.first()) >>> Human.objects.create(transport=Plane.objects.first()) >>> Human.objects.create(transport=Horse.objects.first()) >>> # Выбрать все объекты транспорта можно через класс Transport >>> Transport.objects.all().values() [{'max_speed': 130, 'capacity': 5, u'id': 1}, {'max_speed': 1100, 'capacity': 80, u'id': 2}, {'max_speed': 10, 'capacity': 1, u'id': 3}] >>> # теперь рассмотрим как нам в модели человека получить его транспорт >>> human = Human.objects.first() >>> human.transport >>> # через Transport можно также получить и модели, которые наследуются от данного транспорта т.е. лошадь, самолёт или авто. Делается это через доступ к одноимённым атрибутам в экземпляре модели. Проверить какого типа данный транспорт можно воспользовавшись проверкой на hasattr по каждому наследуемому классу >>> hasattr(human.transport, 'car') True >>> hasattr(human.transport, 'car'), hasattr(human.transport, 'horse'), hasattr(human.transport, 'plane') (True, False, False) >>> # получить объект Car >>> human.transport.car >>> # выбрать всех владельцев авто >>> Human.objects.filter(transport__car__isnull=False) [] >>> # получить владельца авто >>> car = Car.objects.first() >>> car.human

Комментариев нет:

Отправить комментарий