Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to model one way one-to-one relationship in Django

I want to model an article with revisions in Django:

I have following in my article's models.py:

class Article(models.Model):
    title = models.CharField(blank=False, max_length=80)
    slug = models.SlugField(max_length=80)

    def __unicode__(self):
        return self.title

class ArticleRevision(models.Model):
    article = models.ForeignKey(Article)
    revision_nr = models.PositiveSmallIntegerField(blank=True, null=True)
    body = models.TextField(blank=False)

On the artlcle model I want to have 2 direct references to a revision - one would point to a published revision and another to a revision that is being actively edited. However from what I understand, OneToOne and ForeignKey references generate a backreference on the other side of the model reference, so my question is, how do i create a one-way one-to-one reference in Django?

Is there some special incantation for that or do I have to fake it by including state into revision and custom implementations of the fields that ask for a revision in specific state?

Edit: I guess, I've done somewhat poor job of explaining my intent. Let's try it on a higher abstraction level:

My original intent was to implement a sort of revisioned article model, where each article may have multiple revisions, where one of those revisions may be "published" and one actively edited.

This means that the article will have one-to-many relationship to revisions (represented by ForeignKey(Article) reference in ArticleRevision class) and two one way references from Article to revision: published_revision and edited_revision.

My question is mainly, how can I model this with Django's ORM.

like image 960
Roland Tepp Avatar asked Jan 24 '23 10:01

Roland Tepp


1 Answers

The back-references that Django produces are programatic, and do not affect the underlying Database schema. In other words, if you have a one-to-one or foreign key field on your Article pointing to your Revision, a column will be added to the Article table in the database, but not to the Revision table.

Thus, removing the reverse relationship from the revision to the article is unnecessary. If you really feel strongly about it, and want to document in your code that the backlink is never used, a fairly common Django idiom is to give the fields a related_name attribute like _unused_1. So your Article model might look like the following:

class Article(models.Model):
    title = models.CharField(blank=False, max_length=80)
    slug = models.SlugField(max_length=80)
    revision_1 = models.OneToOneField(ArticleRevision, related_name='_unused_1')
    revision_2 = models.OneToOneField(ArticleRevision, related_name='_unused_2')

    def __unicode__(self):
        return self.title

That said, it's rare that a one-to-one relationship is actually useful in an application (unless you're optimizing for some reason) and I'd suggest carefully reviewing your DB schema to make sure this is really what you want. It may make sense to keep a single ForeignKey field on your ArticleRevision pointing back to an Article (since an ArticleRevision will, presumably, always need to be associated with an Article) and adding another column to Revision indicating whether it's published.

like image 193
mmalone Avatar answered Jan 25 '23 23:01

mmalone