Найти - Пользователи
Полная версия: связи в django-ORM
Начало » Django » связи в django-ORM
1 2
Bdfy1
Как заставить Django не делать тот бред ( т е каскадные удаления апдейты и прочее ) который django пытается сделать считая себя умнее чем связи в БД при удалении записи ?
Александр Кошелев
А вы хотите данные неконсистентными сделать?
Bdfy1
Я хочу чтобы целостностью данных занималась БД. Т е когда хочу удалить свойство чтобы не удалялось каскадом полбазы в то время как в базе там стоит on delete restrict, а django мне пытается подсунуть on cascade delete. Кстати конечно хорошо написать столько кода который этот процесс эмулировал но нафига было это делать ? Уж лучше бы добавили много других полезных фич.
Evg
Очень вероятно у вас какой-то косяк в проектировании моделей)

Но можно проставить значение ключа в None перед удалением, например.

Или еще что-то тут:
http://docs.djangoproject.com/en/dev/ref/models/relations/
QuerySet.remove
QuerySet.clear
Bdfy1
Очень вероятно у вас какой-то косяк в проектировании моделей)
Вы о чем ? Представьте 3 сущности - свойсва + модель+типы свойста. Соотв в интерфейсе на джанго 3 таблицы. В базе стоит соотв чтобы при попытке удалить тип свойства “отсылалось лесом”. В интерфейсе можно конечно проверять при попытке удаления но извините зачем это делать когда в базе все это учтены. И тут вмешивается великий ORM от Django который пытается этот “недостаток” устранить. В результате полбазы нет т к была попытка удалить тип свойства. В чем косяк то ?
Вон кстати тикет висит до сих пор:
http://code.djangoproject.com/ticket/7539

А решение хакообразное:

technology = models.ForeignKey(Technology,null=True, blank=True)
Evg
Это еще спорный вопрос кто за целостностью должен следить бд или орм, если вы используете орм)
а какой смысл от свойств у которых нет типа?) по моему логично удаляет)
Александр Кошелев
Bdfy1
В чем косяк то ?
В ДНК.

Если не хотите чтобы типы свойств удалялись, то не удаляете. Перегрузите методы и сделайте чтобы они не удаляли объект.

Такая логика в БД – зло.

Bdfy1
А решение хакообразное:

technology = models.ForeignKey(Technology,null=True, blank=True)
Где здесь хак?
Bdfy1
де здесь хак?
В том что связи предполагаются null что в общем-то не так. Если уж django ORM захотело копировать ON CASCADE DELETE то зачем это делать по умолчанию а не сделать опцией ? Причем если посмотреть генерируемые sql запросы при таком удалении - много интересного там можно найти :).

В ДНК.


Такая логика в БД – зло.
ДНК не ДНК но если с БД работает не только один интерфейс а что-то еще написанное на чем угодно - куда же еще такую логику не засунуть как в БД.

Если не хотите чтобы типы свойств удалялись, то не удаляете. Перегрузите методы и сделайте чтобы они не удаляли объект.
Я хочу чтобы типы свойств удалялись только те которые можно удалить - т е например типы которые не используются в моделях. Вот и все.
regall
Bdfy1, если вашу БД используют другие программы кроме django, причем во всякой извращенной форме (имеются в виду особенности, которые вы описали в предыдущем посте), то вам лучше смотреть в сторону SQLAlchemy, и отказаться от django в пользу, например, TG, или Pylons, которые с алхимией работают по умолчанию и рекомендациям. Все-таки django-ORM разрабатывался как часть django, а не как отдельный ORM, он удовлетворяет тем требованиям, которые на него возложены, и никаким другим.
sorokin_in_ua
Ответы местных гуру просто впечатляют. Такое ощущение что никто ни разу не использовал ON DELETE RESTRICT:
* Evg: Очень вероятно у вас какой-то косяк в проектировании моделей)
* Daevaorn: Косяк в … ДНК
* Daevaorn: Такая логика в БД – зло.
* regall: вашу БД используют другие программы кроме django, причем во всякой извращенной форме

Тут хочется заметить что если ON DELETE RESTRICT явно ( mysql, postgresql ) или неявно ( oracle, mssql ) присутствует в большинстве нормальных баз, то наверное он злом или ошибкой в днк являться не может. Это так же глупо, как утверждать что крестовых отверток не существует, есть только плоские, только потому что вы не хотите пользоваться крестовой ( ужос ) :) А касательно кривости структуры и применимости этого поведения - ну вот хочу я чтобы запись можно было удалить только если на неё нет ссылок, мне удавиться или использовать другой фрейворк?

Отдельно порадовал ответ regall - “Все-таки django-ORM разрабатывался как часть django, а не как отдельный ORM, он удовлетворяет тем требованиям, которые на него возложены, и никаким другим.” Фраза ни о чем и полностью отражает подпись под этим постом. Кстати, было бы неплохо посмотреть на ссылочку, где расписана цель и задачи джанго орм.

Ответ lorien вообще является образцом “дружелюбия”. Новичкам не рады?

Да, собственно о чем я… Я не ругаться и не высказывать свое фи пришел. Просто каждый раз когда искал решение по эмуляции рестрикта наталкивался именно на эту страничку, потому решил на фоне прочих “фи” запечатлеть таки действенное решение - мало ли кто еще набредет. Впрочем, решений тонна.

Исходная задача - эмуляция ON DELETE RESTRICT, что в переводе на русский - запретить удаление объектов, на которые есть ссылки.

1. Перегрузить метод delete у models.Manager

Реализуемо, но меньше всего хотелось бы этим заниматься, поскольку model.Manager это практически полностью прокси к QuerySet, и фактически прийдется переписывать соответствующий метод именно у queryset, а там все не так тривиально, хоть и возможно. Вобщем отмел как геморный.

2. Перегрузить метод delete у models.Model

А вот тут как раз ничего сложного. Оригинальный код:

    def delete(self, using=None):
using = using or router.db_for_write(self.__class__, instance=self)
assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)

# Find all the objects than need to be deleted.
seen_objs = CollectedObjects()
self._collect_sub_objects(seen_objs)

# Actually delete the objects.
delete_objects(seen_objs, using)
Его без проблем можно скопировать в класс-наследник и вставить проверку на количество seen_objs с выбросом исключения в случае > 1 .

Потенциальных минусов тут два:
а) если в будущем код delete из базового класса изменится, то наш может стать не актуальным.
б) Кажется была проблема с вызовом mode.delete() из queryset.delete() в некоторых случаях и я так и не понял решили её или нет.

3. Хуки\сигналы наше все.

Нужно определить какой-то универсальный обработчик удаления и приаттачить его ко всем моделям - будет счастье. Причем “счастье” лишено минусов из пункта 2, но обладает своим - снижение наглядности. Сам сигнал объявляется отдельно от модели и можно банально его не заметить при небрежном просмотре кода. Впрочем, кто сказал что к коду нужно относиться небрежно :)

Ну и собственно пример такого обработчика:
from django.db.models.signals import pre_delete
from django.db.models.query_utils import CollectedObjects
from django.db.utils import IntegrityError

def on_delete_restrict( sender, **kwargs ):
# Find all the objects than need to be deleted.
seen_objs = CollectedObjects()
kwargs['instance']._collect_sub_objects(seen_objs)
if len(seen_objs.data) > 1:
raise IntegrityError('Some objects are still depends on %s(pk=%s)' % ( str(kwargs['instance'].__class__.__name__), str(kwargs['instance'].pk) ) )


pre_delete.connect(on_delete_restrict, sender = Model1)
pre_delete.connect(on_delete_restrict, sender = Model2)
pre_delete.connect(on_delete_restrict, sender = Model3)
Итого - для эмуляции ON DELETE RESTRICT потребутся определить один обработчик и подкинуть его на pre_delete для _нужных_ моделей.

Что тут можно добавить… Вообще эмуляция on_delete_restrict не отменяет необходимости обработки соответствующей ситуации, дабы банально не выдавать юзеру непонятные мессаги. С другой стороны, теперь мы спокойны что юзер по тупняку не грохнет полбазы при удалении, как ему показалось, лишней записи из таблицы-справочника.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB