Добавление виджета ManyToManyWid к полю ManyToManyField в Django Admin - PullRequest
28 голосов
/ 27 марта 2012

Допустим, у меня есть простое приложение для блогов в Django 1.4:

class Post(models.Model):
    title = …
    published_on = …
    tags = models.ManyToManyField('Tag')

class Tag(models.Model):
    name = …

т.е. сообщение имеет много тегов. На администратора Django, я получаю немного <select multi>, если я включаю tags в fields для PostAdmin. Есть ли простой способ включить список сообщений (как простой <select multi>) в TagAdmin? Я попытался вставить fields = ['name', 'posts'] в TagAdmin и получил ошибку ImproperlyConfigured. (тот же результат для post_set).

Я в порядке с Django, так что я могу создать подходящий объект AdminForm и Admin, но я надеюсь, что для этого найдется правильный путь.

Ответы [ 5 ]

27 голосов
/ 31 января 2014

Это можно сделать с помощью пользовательской формы.

from django.contrib import admin
from django import forms

from models import Post, Tag

class PostAdminForm(forms.ModelForm):
    tags = forms.ModelMultipleChoiceField(
        Tag.objects.all(),
        widget=admin.widgets.FilteredSelectMultiple('Tags', False),
        required=False,
    )

    def __init__(self, *args, **kwargs):
        super(PostAdminForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            self.initial['tags'] = self.instance.tags.values_list('pk', flat=True)

    def save(self, *args, **kwargs):
        instance = super(PostAdminForm, self).save(*args, **kwargs)
        if instance.pk:
            instance.tags.clear()
            instance.tags.add(*self.cleaned_data['tags'])
        return instance

class PostAdmin(admin.ModelAdmin):
    form = PostAdminForm

admin.site.register(Post, PostAdmin)

То, что False там может быть заменено на True, если вы хотите вертикально сложенный виджет.

12 голосов
/ 02 ноября 2012

Немного опоздал на вечеринку, но это решение, которое работает для меня (без магии):

# admin.py

from django.contrib import admin
from models import Post

class TagPostInline(admin.TabularInline):
    model = Post.tags.through
    extra = 1

class PostAdmin(admin.ModelAdmin):
    inlines = [TagPostInline]

admin.site.register(Post, PostAdmin)

Ссылка: https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models

4 голосов
/ 06 января 2016

Решение Мэтью у меня не сработало (Django 1.7) при создании новой записи, поэтому мне пришлось немного ее изменить.Надеюсь это кому-нибудь пригодится :) 1001

class PortfolioCategoriesForm(forms.ModelForm):
    items = forms.ModelMultipleChoiceField(
        PortfolioItem.objects.all(),
        widget=admin.widgets.FilteredSelectMultiple('Portfolio items', False),
        required=False
    )

    def __init__(self, *args, **kwargs):
        super(PortfolioCategoriesForm, self).__init__(*args, **kwargs)
        if self.instance.pk:
            initial_items = self.instance.items.values_list('pk', flat=True)
            self.initial['items'] = initial_items

    def save(self, *args, **kwargs):
        kwargs['commit'] = True
        return super(PortfolioCategoriesForm, self).save(*args, **kwargs)

    def save_m2m(self):
        self.instance.items.clear()
        self.instance.items.add(*self.cleaned_data['items'])
0 голосов
/ 11 января 2019

Проверьте мой ответ там для отображения виджета админки ManyToMany в обоих направлениях:

https://stackoverflow.com/a/54147741/3433137

0 голосов
/ 14 апреля 2016

Измените ваши модели, добавив обратное поле:

# models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    published_on = models.DateTimeField()
    tags = models.ManyToManyField('Tag')

class Tag(models.Model):
    name = models.CharField(max_length=10)
    posts = models.ManyToManyField('blog.Post', through='blog.post_tags')

Затем стандартным способом добавьте поле в ModelAdmin:

#admin.py
from django.contrib import admin

class TagAdmin(admin.ModelAdmin):
    list_filter = ('posts', )

admin.site.register(Tag, TagAdmin)
...