Django admin много-ко многим промежуточным моделям, использующим сквозные = и filter_horizontal
Вот как выглядят мои модели:
class QuestionTagM2M(models.Model):
tag = models.ForeignKey('Tag')
question = models.ForeignKey('Question')
date_added = models.DateTimeField(auto_now_add=True)
class Tag(models.Model):
description = models.CharField(max_length=100, unique=True)
class Question(models.Model):
tags = models.ManyToManyField(Tag, through=QuestionTagM2M, related_name='questions')
Все, что я действительно хотел сделать, это добавить временную метку, когда было создано заданное отношение manytomany. Это имеет смысл, но это также добавляет немного сложности. Помимо удаления функциональности .add() [несмотря на то, что единственное поле, которое я действительно добавляю, автоматически создается, поэтому технически это не должно мешать этому]. Но я могу жить с этим, так как я не возражаю против добавления дополнительного QuestionTagM2M.objects.create(question=,tag=)
, если это означает получение дополнительной функциональности временной метки. Моя проблема заключается в том, что мне очень хотелось бы сохранить мой виджет javascript filter_horizontal
в админе. Я знаю, что документы говорят, что я могу использовать встроенную строку вместо этого, но это слишком громоздко, потому что в любом случае нет дополнительных полей, которые действительно были бы во встроенном ключе, кроме внешнего ключа, в Tag
. Кроме того, в более крупной схеме моей схемы базы данных мои объекты Question
уже отображаются как встроенные на моей странице администратора, а поскольку Django не поддерживает вложенные строки в admin [пока], у меня нет способа выбрать теги для данного вопроса. Есть ли способ переопределить formfield_for_manytomany(self, db_field, request=None, **kwargs)
или что-то подобное, чтобы разрешить использование виджета filter_horizontal
и автоматического создания столбца date_added
в базе данных? Это похоже на то, что django должен иметь возможность делать изначально, если вы укажете, что все столбцы промежуточного элемента автоматически создаются (кроме внешних), возможно, с помощью auto_created=True
? или что-то похожее
Ответы
Ответ 1
Есть способы сделать это
- Как предоставлено @obsoleter в комментарий ниже: установите
QuestionTagM2M._meta.auto_created = True
и обработайте проблемы w/syncdb.
-
Динамически добавьте поле date_added
в модель M2M модели Question
в models.py
class Question(models.Model):
# use auto-created M2M model
tags = models.ManyToMany(Tag, related_name='questions')
# add date_added field to the M2M model
models.DateTimeField(auto_now_add=True).contribute_to_class(
Question.tags.through, 'date_added')
Тогда вы можете использовать его в admin как обычно ManyToManyField
.
В оболочке Python используйте Question.tags.through
для ссылки на модель M2M.
Примечание. Если вы не используете South
, то syncdb
достаточно; Если вы это сделаете, South
не нравится
таким образом и не будет замораживать поле date_added
, вам нужно вручную записать миграцию, чтобы добавить/удалить соответствующий столбец.
-
Настроить ModelAdmin:
- Не определяйте
fields
внутри настраиваемого ModelAdmin, задайте filter_horizontal
. Это будет обходить проверку поля, упомянутую в ответе Ирфана.
- Настроить
formfield_for_dbfield()
или formfield_for_manytomany()
, чтобы администратор Django использовал widgets.FilteredSelectMultiple
для поля tags
.
- Настроить метод
save_related()
внутри класса ModelAdmin, например
def save_related(self, request, form, *args, **kwargs):
tags = form.cleaned_data.pop('tags', ())
question = form.instance
for tag in tags:
QuestionTagM2M.objects.create(tag=tag, question=question)
super(QuestionAdmin, self).save_related(request, form, *args, **kwargs)
- Кроме того, вы можете исправить
__set__()
дескриптора поля ReverseManyRelatedObjectsDescriptor
для ManyToManyField для date_added
, чтобы сохранить экземпляр M2M без исключения.
Ответ 2
Документы могут быть изменены с момента публикации предыдущих ответов. Я взглянул на ссылку django docs, о которой упоминал @Irfan, и это кажется более прямым, чем раньше.
Добавьте встроенный класс в admin.py
и установите модель в модель M2M
class QuestionTagM2MInline(admin.TabularInline):
model = QuestionTagM2M
extra = 1
установите inlines
в свой класс администратора, чтобы содержать только что определенную строку Inline
class QuestionAdmin(admin.ModelAdmin):
#...other stuff here
inlines = (QuestionTagM2MInline,)
Не забудьте зарегистрировать этот класс администратора
admin.site.register(Question, QuestionAdmin)
После выполнения вышеописанного, когда я нажимаю на вопрос, у меня есть форма, чтобы делать все обычные изменения на нем и ниже, это список элементов в моей связи m2m, где я могу добавлять записи или редактировать существующие.
Ответ 3
Из https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-intermediary-models
Когда вы укажете промежуточную модель, используя сквозной аргумент для ManyToManyField, администратор не будет отображать виджет по умолчанию. Это связано с тем, что каждый экземпляр этой промежуточной модели требует больше информации, чем может отображаться в одном виджете, а макет, необходимый для нескольких виджетов, будет зависеть от промежуточной модели.
Однако вы можете попытаться включить поле тегов явно, используя fields = ('tags',)
в admin. Это приведет к этому исключению проверки
"QuestionAdmin.fields" не может включать теги поля ManyToManyField, потому что "теги" вручную задает "сквозную" модель.
Эта проверка выполняется в https://github.com/django/django/blob/master/django/contrib/admin/validation.py#L256
if isinstance(f, models.ManyToManyField) and not f.rel.through._meta.auto_created:
raise ImproperlyConfigured("'%s.%s' "
"can't include the ManyToManyField field '%s' because "
"'%s' manually specifies a 'through' model." % (
cls.__name__, label, field, field))
Я не думаю, что вы можете обойти эту проверку, если вы не реализуете свое собственное поле, которое будет использоваться как ManyToManyField.