Django Admin — отображение промежуточных полей для моделей M2M

У нас есть приложение Django, которое содержит список газетных статей. Каждая статья имеет отношения m2m как с «представителем», так и с «фирмой» (компанией, упомянутой в статье).

На данный момент страница «Добавить статью» для создания новых статей очень близка к тому, что нам нужно — это просто стандартная администрация Django, и мы используем filter_horizontal для установки двух отношений m2m.

Следующим шагом было добавление поля «рейтинг» в качестве промежуточного поля для каждого отношения m2m.

Итак, пример нашего models.py

class Article(models.Model):
    title = models.CharField(max_length=100)
    publication_date = models.DateField()
    entry_date = models.DateField(auto_now_add=True)
    abstract = models.TextField() # Can we restrict this to 450 characters?
    category = models.ForeignKey(Category)
    subject = models.ForeignKey(Subject)
    weekly_summary = models.BooleanField(help_text = 'Should this article be included in the weekly summary?')
    source_publication = models.ForeignKey(Publication)
    page_number = models.CharField(max_length=30)
    article_softcopy = models.FileField(upload_to='article_scans', null=True, blank=True, help_text='Optionally upload a soft-copy (scan) of the article.')
    url = models.URLField(null=True, blank=True, help_text = 'Enter a URL for the article. Include the protocl (e.g. http)')
    firm = models.ManyToManyField(Firm, null=True, blank=True, through='FirmRating')
    spokesperson = models.ManyToManyField(Spokeperson, null=True, blank=True, through='SpokespersonRating')

    def __unicode__(self):
        return self.title

class Firm(models.Model):
    name = models.CharField(max_length=50, unique=True)
    homepage = models.URLField(verify_exists=False, help_text='Enter the homepage of the firm. Include the protocol (e.g. http)')

    def __unicode__(self):
        return self.name

    class Meta:
        ordering = ['name']

class Spokeperson(models.Model):
    title = models.CharField(max_length=100)
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)

    def __unicode__(self):
        return self.first_name + ' ' + self.last_name

    class Meta:
        ordering = ['last_name', 'first_name']

class FirmRating(models.Model):
    firm = models.ForeignKey(Firm)
    article = models.ForeignKey(Article)
    rating = models.IntegerField()

class SpokespersonRating(models.Model):
    firm = models.ForeignKey(Spokesperson)
    article = models.ForeignKey(Article)
    rating = models.IntegerField()

Проблема здесь в том, что как только мы изменим поле «Фирма и представитель» на «через» и воспользуемся посредниками, на нашей странице «Добавить статью» больше не будет элемента управления filter_horizontal для добавления отношений «Фирмы/представители» в статью — они полностью исчезнут. Их вообще не видно. Я понятия не имею, почему это так.

Я надеялся, что будет какой-то способ продолжать использовать классный виджет filter_horizontal для установки отношения и каким-то образом просто встроить еще одно поле ниже этого для установки рейтинга. Однако я не уверен, как это сделать, все еще используя администратора Django.

Я видел здесь статью о переопределении одного виджета в админке Django:

http://www.fictitiousnonsense.com/archives/22

Однако я не уверен, что этот метод все еще действителен, и я не уверен в его применении здесь, с FK к промежуточной модели (тогда это в основном встроенный?).

Наверняка есть простой способ сделать все это?

Здоровья, Виктор


person victorhooi    schedule 24.06.2010    source источник


Ответы (1)


Проблема в том, что метод администратора formfield_for_manytomany в django.contrib.admin.options не возвращает поле формы для многих полей с промежуточной моделью! http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L157

Вам придется переопределить этот метод в вашем ModelAdmin:

def formfield_for_manytomany(self, db_field, request=None, **kwargs):
    """
    Get a form Field for a ManyToManyField.
    """
    # If it uses an intermediary model that isn't auto created, don't show
    # a field in admin.
    if not db_field.rel.through._meta.auto_created:
        return None    # return something suitable for your needs here!
    db = kwargs.get('using')

    if db_field.name in self.raw_id_fields:
        kwargs['widget'] = widgets.ManyToManyRawIdWidget(db_field.rel, using=db)
        kwargs['help_text'] = ''
    elif db_field.name in (list(self.filter_vertical) + list(self.filter_horizontal)):
        kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical))
person Bernhard Vallant    schedule 24.06.2010
comment
Хм, так мне нужно как-то переопределить этот метод, в общем, чтобы ничего не сломать? Ургх, моих знаний о Django здесь немного не хватает. Как люди обычно редактируют промежуточные модели с помощью администратора Django? - person victorhooi; 25.06.2010
comment
Вам нужно только переопределить его для своего поля, вы можете поставить что-то вроде if db_field.name == 'my_field там! - person Bernhard Vallant; 25.06.2010