Django: переопределение метода save (): как вызвать метод delete () дочернего класса - PullRequest
0 голосов
/ 05 марта 2010

Настройка =

У меня есть этот класс, расшифровка:

class Transcript(models.Model):    
    body = models.TextField('Body')
    doPagination = models.BooleanField('Paginate')
    numPages = models.PositiveIntegerField('Number of Pages')

и этот класс, TranscriptPages (models.Model):

class TranscriptPages(models.Model):
    transcript = models.ForeignKey(Transcript)
    order = models.PositiveIntegerField('Order')
    content = models.TextField('Page Content', null=True, blank=True)

Поведение администратора, которое я пытаюсь создать, состоит в том, чтобы позволить пользователю заполнить Transcript.body всем содержимым длинного документа, и, если он установит Transcript.doPagination = True и сохранит администратора Transcript, я автоматически разделю тело в n страниц стенограммы.

В админке TranscriptPages является StackedInline администратора транскрипта.

Для этого я переопределяю метод сохранения Transcript:

def save(self):
    if self.doPagination:
        #do stuff
        super(Transcript, self).save()
    else:
        super(Transcript, self).save()

Проблема =

Когда Transcript.doPagination имеет значение True, я хочу вручную удалить все TranscriptPages, которые ссылаются на этот Transcript, чтобы затем я мог создать их заново с нуля.

Итак, я думал, что это будет работать:

#do stuff   
TranscriptPages.objects.filter(transcript__id=self.id).delete()
super(Transcript, self).save()

но когда я пытаюсь, я получаю эту ошибку:

Тип исключения: ValidationError Значение исключения: [u'Выберите действительный выбор. Этот выбор не является одним из доступные варианты. ']

... и это последняя вещь в трассировке стека до возникновения исключения:

... / django / forms / models.py в save_existing_objects

  1. pk_value = form.fields [pk_name] .clean (raw_pk_value)

Другие попытки исправить:

  • t = self.transcriptpages_set.all (). Удалить () (где self = стенограмма из метод save ())
  • зацикливание на t (выше) и удаление каждого элемента в отдельности
  • отправка сигнала post_save на TranscriptPages, который вызывает метод удаления

Есть идеи? Как админ это делает?

ОБНОВЛЕНИЕ: Время от времени, пока я играюсь с кодом, я могу получить различную ошибку (ниже), но затем она просто исчезает , и я не могу повторить ее снова. .. до следующего случайного времени.

Тип исключения:
MultiValueDictKeyError Exception Значение: «Ключ» transcriptpages_set-0-id ' не найден в Местоположение исключения: ... / django / utils / datastructures.py в getitem , строка 203

и последние строки из трассы:

... / django / forms / models.py в _construct_form

  1. form = super (BaseInlineFormSet, self) ._ construct_form (i, ** kwargs)

... / django / utils / datastructures.py в getitem

  1. pk = self.data [pk_key]

Ответы [ 3 ]

1 голос
/ 13 июня 2010

В конце концов, это был вопрос времени. При удалении только одного дочернего объекта из многих проблем не было. Если бы я удалял слишком много дочерних объектов одновременно, ошибка могла произойти, потому что действие удаления пыталось ссылаться на идентификаторы, которых не было рядом. Вот почему ничего не работает, ни сигналы, ни [объект] _set. Я исправил это с помощью jquery, чтобы установить скрытую переменную в форме редактирования для дочернего объекта, в результате чего объект сначала обработал обновление (замедлило его обработку), а затем удалил.

0 голосов
/ 20 апреля 2012

Другим возможным решением может быть переопределение save_formset () в ModelAdmin для полного предотвращения обновления:

def save_formset(self, request, form, formset, change):
     if (form.cleaned_data['do_pagination'] and 
            formset.model == TranscriptPages):
        formset.changed_objects = []
        formset.new_objects = []
        formset.deleted_objects = []
    else:
        formset.save()

Затем вы можете делать все, что захотите, в Modal.save ().Обратите внимание, что, вероятно, существует более элегантный / защищенный от разрывов способ остановить обработку набора форм, если кто-то захочет покопаться во внутренних деталях.

0 голосов
/ 05 марта 2010

Возможно, вы пытаетесь получить доступ к Transaction.id до того, как он был создан. Кроме того, вы можете попытаться получить доступ к объектам TransactionPage через объект Transaction с помощью Transactionspage_set (см. документацию по запросу о записи FOO_set ).

def save(self):
    super(Transcript, self).save()
    if self.doPagination:
        self.transaction_set.all().delete() # .all() may be optional
        pages = ... # whatever you do to split the content
        self.numPages = len(pages)
        self.save() # yes, a second save if you store numPages in Transaction
        for page_number in range(self.numPages):
            TransactionPage.objects.create(content=pages[page_number], 
                                           order=page_number, transaction=self)

Вы также можете переключиться на сохранение numPages в транзакции и получить к нему доступ через свойство.

class Transaction(models.Model):
    # ...
    # replace numPages with
    @property
    def page_count(self):
        return self.transactionpage_set.count() or 1

И затем, если вы сделаете еще один шаг вперед, вы всегда сможете использовать объекты TransactionPage для отображения. Это позволит вам избавиться от дополнительного вызова self.save() в приведенном выше методе save (). Это также позволит вам упростить ваши шаблоны, всегда отображая TransactionPage.content вместо условного отображения Transaction.body, если вы разбиваете на страницы, и TransactionPage.content в противном случае.

class Transaction(models.Model):
    body = models.TextField()
    paginate = models.BooleanField()

    @property
    def page_count(self):
        return self.transactionpage_set.count() # no more "or 1" cruft!

    def save(self):
        super(Transcript, self).save()
        self.transaction_set.delete() # might need an .all() before .delete()
        if self.paginate:
            # Do whatever you do to split the body into pages.
            pages = ... 
        else:
            # The only page is the entire body.
            pages = [self.body]
        for (page_number, page_content) in enumerate(pages):
            TransactionPage.objects.create(content=page_content, 
                                           order=page_number, transaction=self)
...