2013-05-24 3 views
6

я следующие модели:Django Много-ко-многим контроля вставки отношение

class Item(models.Model): 
    # fields 
    # ... 

class Collection(models.Model): 
    items = models.ManyToManyField(Item, related_name="collections") 
    # other fields 
    # ... 

Теперь я хочу две вещи:

  1. я хочу, чтобы контролировать, если Item может быть добавлен к Collection ,
  2. Я хочу, чтобы Collection обновил некоторые его поля, если был добавлен или удален Item.

Для второй проблемы я знаю, что есть django.db.models.signals.m2m_changed, который я могу использовать для изменения отношений. Разрешено ли/ok изменять Collection в обратном вызове сигнала? Могу ли я использовать сигнал также для «прерывания» вставки для проблемы 1?

+0

Для проблемы 1 вы, вероятно, должны использовать цикл очистки формы для проверки данных (что облегчает обмен сообщениями проверки), которое затем отправляется на [save_m2m] (https: //docs.djangoproject.com/en/dev/themes/forms/modelforms/# the-save-method) –

+1

@Hedde: Я бы предпочел решение, близкое к моделям, потому что мои данные скорее всего не будут изменены из формы. (Скорее всего, с помощью инструментов CLI и открытого API). – Constantinius

+0

Вы можете перезаписать методы сохранения модели, по крайней мере, для части логики, но если вы реализуете API, кажется, что такая логика принадлежит уровню авторизации API. Tastypie - отличный богатый API, который хорошо сочетается с Django. –

ответ

8

Я думаю, что лучший способ приблизиться оба ваших желаемого поведения не с сигналами, а с переопределяется сохранить() и удалить() метод на through столе, который вы бы определить в явном виде с помощью аргумента through см.: https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ManyToManyField.through. и это: https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-predefined-model-methods

Что-то вроде этого:

# -*- coding: utf-8 -*- 

from django.db import models 


class Item(models.Model): 
    # fields 
    # ... 

class Collection(models.Model): 
    items = models.ManyToManyField(Item, related_name="collections", through="CollectionItem") 
    # other fields 
    # ... 

class CollectionItem(models.Model): 
    collection = models.ForeignKey(Collection) 
    item = models.ForeignKey(Item) 

    def save(self, *args, **kwargs): 
     # Only allow this relationship to be created on some_condition 
     # Part 1 of your question. 
     if some_condition: 
      super(CollectionItem, self).save(*args, **kwargs) 

      # Update some fields on Collection when this 
      # relationship is created 
      # Part 2 of your question (1/2) 
      self.Collection.updateSomeFields() 

    def delete(self, *args, **kwargs): 
     collection = self.collection 
     super(CollectionItem, self).delete(*args, **kwargs) 

     # Update some fields on Collection when this relationship 
     # is destroyed. 
     # Part 2 of your question (2/2) 
     collection.updateSomeFields() 

Кстати, вы обнаружите, что добавление отношения будет причины сохранения сигнала, на этом счете модели.

И в отношении сигналов, когда у вас есть сквозная таблица на месте, вы сможете слушать сигналы pre_save и/или post_save, но ни один из них не позволит вам прямо наложить вето на создание отношений.

Если одна или обе модели предоставлены третьей стороной, и вы действительно не можете создать сквозную таблицу, тогда да, маршрут сигнала может быть единственным способом.

https://docs.djangoproject.com/en/dev/ref/signals/#m2m-changed

В этом случае, вы можете слушать для m2m_changed события и запуска обновления ваших объектов коллекции (часть 2 вашего вопроса) и задним числом удалить ненадо отношения (созданные нами часть 1 вашего вопроса). Тем не менее, поскольку этот последний бит является уродливым kludgy, я бы придерживался явной сквозной таблицы, если можно.

+1

Мне очень нравится такой подход. Я никогда не думал о том, чтобы использовать модель «trhough» как место для обработки, но это имеет смысл. Единственный недостаток, который я вижу, заключается в том, что теперь я не могу использовать метод 'add' для' ManyToManyField', но мне нужно создать 'CollectionItem'. Это не проблема, просто к чему привыкнуть. Спасибо за Ваш ответ. – Constantinius

+1

это тоже хорошая альтернатива и обеспечивает большую гибкость. – Alp

+1

Это особенно полезно, поскольку, поскольку сигнал m2m_changed не срабатывает для изменений, внесенных в admin [https://code.djangoproject.com/ticket/16073] – trubliphone

3
  1. Сигнал pre_save вызывается перед сохранением экземпляра. Но вы не можете отменить операцию сохранения оттуда. Лучшее решение было бы добавить новый метод к вашей модели Collection, который отвечает за проверку, если Item может быть добавлен:

    class Collection(models.Model): 
        items = models.ManyToManyField(Item, related_name="collections") 
    
        ... 
    
        def add_item(self, item): 
         if check_if_item_can_be_added(item): 
          items.add(item) 
          self.save() 
    
    def check_if_item_can_be_added(self, item): 
        # do your checks here 
    
  2. При добавлении экземпляра в поле m2m, сохранение методы не получают называется. Вы правы, сигнал m2m_changed - это путь. Вы можете безопасно обновить экземпляр коллекции там.

Смежные вопросы