django - админовские модели «многие ко многим», использующие through = и filter_horizontal

Вот как выглядят мои модели:

<code>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')
</code>

Все, что я действительно хотел сделать, это добавить временную метку, когда было создано данное множество отношений. Это имеет смысл, но это также добавляет немного сложности. Помимо удаления функции .add () [несмотря на тот факт, что единственное поле, которое я действительно добавляю, создается автоматически, поэтому оно технически больше не должно мешать этому]. Но я могу жить с этим, так как я не против делать дополнительныеQuestionTagM2M.objects.create(question=,tag=) вместо этого, если это означает получение дополнительной функциональности метки времени. Моя проблема в том, что я действительно хотел бы сохранить своиfilter_horizontal Виджет JavaScript в админке. Я знаю, что в документах говорится, что вместо этого я могу использовать inline, но это слишком громоздко, потому что нет никаких дополнительных полей, которые могли бы быть внутри inline, кроме внешнего ключа дляTag тем не мение. Кроме того, в большей схеме моей схемы базы данных мойQuestion объекты уже отображаются как встроенные на моей странице администратора, и, поскольку Django не поддерживает вложенные встроенные строки в admin [пока], у меня нет возможности выбрать теги для данного вопроса. Есть ли способ переопределитьformfield_for_manytomany(self, db_field, request=None, **kwargs) или что-то подобное, чтобы учесть мое изящноеfilter_horizontal виджет и автоматическое созданиеdate_added столбец в базу данных? Это похоже на то, что django должен иметь возможность делать изначально, если вы укажете, что все столбцы в промежуточном звене создаются автоматически (кроме внешних ключей), возможно, сauto_created=True? или что-то подобное

 Chris Wesseling16 апр. 2012 г., 19:47
tag_set а такжеquestion_set простоManagers, Вы можете продлитьdjango.db.models.Manager и добавить все суперспособности, которые вы хотите к нему. Если вы объявите их с именами по умолчанию на своих моделях, они не будут перезаписаны именами по умолчанию, Django не будет вам мешать. Я использовал это, чтобы изменить поведение.objects. Менеджер некоторых из моих моделей.

Ответы на вопрос(3)

Решение Вопроса

ways сделать это

As provided by @obsoleter in the comment below : set QuestionTagM2M._meta.auto_created = True and deal w/ syncdb matters.

Dynamically add date_added field to the M2M model of Question model in 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')

Then you could use it in admin as normal ManyToManyField.
In Python shell, use Question.tags.through to refer the M2M model.

Note, If you don't use South, then syncdb is enough; If you do, South does not like this way and will not freeze date_added field, you need to manually write migration to add/remove the corresponding column.

Customize ModelAdmin:

Don't define fields inside customized ModelAdmin, only define filter_horizontal. This will bypass the field validation mentioned in Irfan's answer. Customize formfield_for_dbfield() or formfield_for_manytomany() to make Django admin to use widgets.FilteredSelectMultiple for the tags field. Customize save_related() method inside your ModelAdmin class, like

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)
Also, you could patch __set__() of the ReverseManyRelatedObjectsDescriptor field descriptor of ManyToManyField for date_added to save M2M instance w/o raise exception.
 27 дек. 2013 г., 20:20
@obsoleter это нарушает syncdb, но вы можете обойти его, если не «syncdb»; в сис.аргв: в мета классе. Все еще хакерский, но лучший способ, которым я это видел.
 13 нояб. 2012 г., 03:33
Я нашел другой вариант, прочитав источник дляBaseModelAdmin.formfield_for_manytomany метод вdjango/contrib/admin/options.py, Вы можете обмануть ModelAdmin, используя настраиваемую таблицу «многие ко многим» для горизонтальных / вертикальных фильтров, установивQuestionTagM2M._meta.auto_created = True
 20 февр. 2013 г., 16:32
@obsoleter Ваш комментарий - лучший ответ в этой теме.

https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-intermediary-models

When you specify an intermediary model using the through argument to a ManyToManyField, the admin will not display a widget by default. This is because each instance of that intermediary model requires more information than could be displayed in a single widget, and the layout required for multiple widgets will vary depending on the intermediate model.

Тем не менее, вы можете попробовать включить поле тегов явно с помощьюfields = ('tags',) в админ. Это вызовет это исключение проверки

'QuestionAdmin.fields' can't include the ManyToManyField field 'tags' because 'tags' manually specifies a 'through' model.

Эта проверка осуществляется в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.

 15 апр. 2012 г., 23:27
Другой возможностью было бы пометить поле date_added как read_only, что позволитModelAdmin пройти валидацию. Очевидно, что вы не можете использовать это, если вы хотите изменитьdate_added на более позднем этапе, но, исходя из вашей логики, вам, похоже, не придется этого делать.
 30 сент. 2015 г., 21:30
Этот ответ больше не верен - документы были обновлены. Пожалуйста, смотрите ответ @Daniel Rucci ниже.

возможно, изменились с момента публикации предыдущих ответов. Я посмотрел на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, где я могу добавлять записи или редактировать существующие.

 19 янв. 2017 г., 05:53
не сохраняетfilter_horizontal админ javascript
 30 сент. 2015 г., 21:29
Этот ответ самый правильный и недавний. Документы теперь подробно описывают, как это сделать. Стоит также отметить, что явное объявление поля теперь работает, как показано в ответе Ирфана.

Ваш ответ на вопрос