@Ber: Я добавил проверку к модели, подобной этому
class Parent(models.Model):
name = models.CharField(max_length=255)
favoritechild = models.ForeignKey("Child", blank=True, null=True)
def save(self, force_insert=False, force_update=False):
if self.favoritechild is not None and self.favoritechild.myparent.id != self.id:
raise Exception("You must select one of your own children as your favorite")
super(Parent, self).save(force_insert, force_update)
, который работает точно, как я хочу, но было бы действительно хорошо, если эта проверка могла бы ограничить выбор в выпадающем в администраторском интерфейсе вместо того, чтобы проверить после выбора.
В зависимости от того, как вы организовали иерархию исключений, повторное генерирование исключения путем присвоения имени переменной исключения в операторе throw может срезать исходный объект исключения.
Выражение throw без аргументов вызовет текущий объект исключения с сохранением его динамического типа, тогда как выражение throw с аргументом вызовет новое исключение, основанное на типе static аргумента для throw
].
Например,
int main()
{
try
{
try
{
throw Derived();
}
catch (Base& b)
{
std::cout << "Caught a reference to base\n";
b.print(std::cout);
throw b;
}
}
catch (Base& b)
{
std::cout << "Caught a reference to base\n";
b.print(std::cout);
}
return 0;
}
Как написано выше, программа выведет:
Caught a reference to base Derived Caught a reference to base Base
Если throw b
заменить на throw
, то внешний catch также поймает первоначально созданное исключение Derived
.Это все еще сохраняется, если внутренний класс перехватывает исключение Base
по значению, а не по ссылке - хотя, естественно, это будет означать, что исходный объект исключения не может быть изменен, поэтому любые изменения в b
будут не должно отражаться в исключительной ситуации Derived
, перехваченной внешним блоком.
Во втором случае согласно C ++ Standard 15.1 / 6 конструктор копирования не используется:
Выражение throw без операнда повторно генерирует обрабатываемое исключение. Исключение повторно активируется с существующим временным; новый объект временного исключения не создается. Исключение больше не считается обнаруженным; следовательно, значение uncaught_exception () снова будет истинным.
В первом случае будет сгенерировано новое исключение в соответствии с 15.1 / 3:
Выражение выброса инициализирует временный объект, называемый объектом исключения, тип из которых определяется путем удаления любых cv-квалификаторов верхнего уровня из статического типа операнда throw и настройки тип от «массив T» или «функция, возвращающая T» до «указатель на T» или «указатель на функцию, возвращающую T», соответственно. <...> Временное значение используется для инициализации переменной, указанной в соответствующем обработчике (15.3). Тип выражения throw не должен быть неполный тип или указатель или ссылка на неполный тип, отличный от void *, const void *, volatile void * или const volatile void *. За исключением этих ограничений и ограничений на сопоставление типов, упомянутое в 15.3, операнд throw обрабатывается точно как аргумент функции при вызове (5.2.2) или операнд оператора возврата.
В обоих случаях конструктор копирования требуется на этапе выброса (15.1 / 5):
Когда брошенный объект является объектом класса, а конструктор копирования используется для инициализировать временную копию невозможно, программа имеет неправильный формат (даже если временный объект иначе можно было бы удалить). Точно так же, если деструктор для этого объекта недоступен, программа имеет неправильный формат (даже если временный объект может быть удален в противном случае).