Ответ 1
Вы можете использовать пользовательский ModelForm в admin (установив атрибут "form" вашего подкласса ModelAdmin). Таким образом, вы делаете это так же в админке, как и в любом другом месте.
Учитывая модель с полями ForeignKeyField (FKF) или ManyToManyField (MTMF) с ключом для "я", как я могу предотвратить самостоятельный (рекурсивный) выбор в Django Admin (admin).
Короче говоря, должно быть возможно предотвратить саморекурсивный выбор экземпляра модели в администраторе. Это применяется при редактировании существующих экземпляров модели, а не при создании новых экземпляров.
Например, возьмите следующую модель для статьи в новостном приложении;
class Article(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField()
related_articles = models.ManyToManyField('self')
Если существует 3 Article
экземпляра (название: a1-3), при редактировании существующего экземпляра Article
через admin поле related_articles
представляется по умолчанию блоком выбора html (multiple), который предоставляет список ВСЕХ статей (Article.objects.all()
). Пользователь должен видеть и иметь возможность выбирать Article
экземпляры, отличные от самого себя, например. При редактировании Article
a1, related_articles
доступно для выбора = a2, a3.
В настоящее время я вижу 3 возможных способа сделать это в порядке уменьшения предпочтений;
related_articles
(через фильтр запроса исключения, например Article.objects.filter(~Q(id__iexact=self.id))
, чтобы исключить текущий экземпляр, редактируемый из списка связанных_артикулов, которые пользователь может видеть и выберите из него. Создание/настройка используемого набора запросов может возникать внутри конструктора (__init__
) пользовательского Article ModelForm
или с помощью какого-либо динамического параметра limit_choices_to Model
. Это потребует способа захвата экземпляра редактируется для использования для фильтрации.save_model
класса Article Model
или ModelAdmin
, чтобы проверить и удалить себя из related_articles
перед сохранением экземпляра. Это все равно означает, что пользователи-администраторы могут видеть и выбирать все статьи, включая редактируемый экземпляр (для существующих статей).В настоящее время идеальное решение (1) можно выполнить с помощью пользовательских форм модели вне админа, так как можно передать в фильтрованную переменную queryset для экземпляра, редактируемого в конструкторе формы модели. Вопрос в том, можете ли вы получить экземпляр Article
, то есть "self", отредактированный администратором, прежде чем форма будет создана для того, чтобы сделать то же самое.
Возможно, я собираюсь сделать это неправильно, но если вам разрешено определять FKF/MTMF для одной и той же модели, тогда должен быть способ, чтобы администратор делал правильные действия - и не позволял пользователю выбрав сам, исключив его в список доступных вариантов.
Примечание: Решение 2 и 3 можно сделать сейчас и предоставить, чтобы попытаться избежать этих ответов, в идеале я хотел бы получить ответ на решение 1.
Вы можете использовать пользовательский ModelForm в admin (установив атрибут "form" вашего подкласса ModelAdmin). Таким образом, вы делаете это так же в админке, как и в любом другом месте.
Карл верен, здесь образец кода вырезания и вставки, который будет идти в admin.py
Я нахожу, что переход к отношениям Django может быть сложным, если у вас нет четкого понимания, а живой пример может стоить на 1000 раз больше, чем "идти читать это" (не то, что вам не нужно понимать, что происходит).
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
id__exact=self.instance.id)
Вы также можете переопределить метод get_form
ModelAdmin следующим образом:
def get_form(self, request, obj=None, **kwargs):
"""
Modify the fields in the form that are self-referential by
removing self instance from queryset
"""
form = super().get_form(request, obj=None, **kwargs)
# obj won't exist yet for create page
if obj:
# Finds fieldnames of related fields whose model is self
rmself_fields = [f.name for f in self.model._meta.get_fields() if (
f.concrete and f.is_relation and f.related_model is self.model)]
for fieldname in rmself_fields:
form.base_fields[fieldname]._queryset =
form.base_fields[fieldname]._queryset.exclude(id=obj.id)
return form
Обратите внимание, что это решение на основе размера, которое автоматически находит самореферентные поля модели и удаляет из них все: -)