Add info to model instance referenced in ManyToMany in Django
From start, I feel I'm missing something simple - if that's so please point me in the right direction. Following is minimal explanation for my problem:
There are models for Journal, Article and Note, the last representing editor's note on published Article. Article can be not published at all, can be published in multiple Journals; also obviously Journals can publish multiple articles. So for start we have:
class Article(models.Model): (author, date of creation, actual text, etc) class Journal(models.Model): (edition number, title, staff, etc) articles = models.ManyToManyField(Article)
So far it's standard. Now I need to be able to add a Note to a published article - let's say a short review or editor's comment. A Note needs to be connected to a single published Article and a certain journal (the same article can have different notes in different journal issues). Finally, a Note might be connected to Journal only and not to any Article - in this case it would represent an issue foreword. So far I have this:
class Note(models.Model): journal = models.ForeignKey(Journal) article = models.ForeignKey(Article, null=True, blank=True) text = models.TextField() (other stuff, irrelevant)
With this setup we know for sure that every Note has Journal reference and might have an Article reference or not. However, now it's possible to add note to an article not published in certain journal and connect it to that 'wrong' journal. And here my head spins.
How to enforce that new note can be created only connected with an article in Journal's article list? (also in django's admin - ideally the dropdown list should show only articles from the selected journal)
And also, how to make sure there is only one note per journal+article combination (+ possibly one without any article associated)?
This can be more easily done via the Form.
To ensure a only valid options at the Model level, requires extra work.
Therefore, I recommend this excellent 3rd party package: django-smart-selects
Example from the Readme.
from smart_selects.db_fields import ChainedForeignKey class Location(models.Model): continent = models.ForeignKey(Continent) country = ChainedForeignKey( Country, chained_field="continent", chained_model_field="continent", show_all=False, auto_choose=True ) area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country") city = models.CharField(max_length=50) street = models.CharField(max_length=100)
I am using it in production with good results. I do recommend setting the JQUERY settings to use a newer version of jQuery.