We have a Django app which contains a list of newspaper articles. Each article has a m2m relationship with both a "spokesperson", as well as a "firm" (company mentioned in the article).
At the moment, the Add Article page for creating new Articles is quite close to what we want - it's just the stock Django Admin, and we're using filter_horizontal for setting the two m2m relationships.
The next step was to add a "rating" field as an intermediary field on each m2m relationship.
So, an example of our 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()
The issue here is that once we change our Firm and Spokesperson field to "through" and use intermediaries, our Add Article page no longer has a filter_horizontal control to add Firms/Spokeperson relationships to the Article - they completely disappear. You can't see them at all. I have no idea why this is.
I was hoping for there to be some way to keep using the cool filter_horizontal widget to set the relationship, and somehow just embed another field below that for setting the rating. However, I'm not sure how to do this whilst still leveraging off the Django admin.
I saw a writeup here about overriding a single widget in Django admin:
http://www.fictitiousnonsense.com/archives/22
However, I'm not sure if that method is still valid, and I'm not sure about applying it to here, with a FK to a intermediary model (it's basically an inline then?).
Surely there's an easy way of doing all this?
Cheers, Victor
The problem is that the admin's method formfield_for_manytomany
in django.contrib.admin.options
doesn't return a form field for manytomany fields with an intermediary model! http://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L157
You would have to override this method in your 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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With