6

I have DataFile models which have LogOutput objects. A DataFile object will have a single LogOutput. For any LogOutput that belongs to a DataFile, it will only belong to a single DataFile. Other models also have LogOutput objects.

Since they are one-to-one, except that LogOutputs can belong to things other than DataFiles (eg. Suites have them too -- see code below) I thought the right thing to do would be to have a OneToOneField defined in DataFile that is the LogOutput.

models.py:

class LogOutput(models.Model):
    raw = models.TextField()

class DataFile(models.Model):
    name = models.CharField()#etc.
    logoutput = models.OneToOneField(LogOutput)

class Suite(models.Model):
    # Many Suites will have the same datafile:
    datafile = models.ForeignKey(DataFile)

    # Each Suite has a unique LogOutput different from the DataFile's
    # and as with the DataFile, that LogOutput will have just one Suite
    logoutput = models.OneToOneField(LogOutput)

Now, when I look at a DataFile in the Admin view, I want to see the LogOutput, so I thought I would just inline it.

admin.py:

class LogOutputInline(admin.TabularInline):
    model = LogOutput

class DataFileAdmin(admin.ModelAdmin):
    search_fields = ['name']
    inlines = [LogOutputInline]

admin.site.register(DataFile, DataFileAdmin)

It appears that because of the directionality of where the OneToOneField(s) are defined, I can't do the inlining. The above admin.py gives me:

<class 'trial.models.LogOutput'> has no ForeignKey to <class 'trial.models.DataFile'>

Which, of course is true, but I don't see how it's relevant, because a DataFile has one (and only one) LogOutput which, in turn, belongs to only this one DataFile.

I read Question 1744203 but the solution there was to reverse the direction of the relationship. I don't think I can do that because other objects (Suites) also have LogOutputs.

And, if it matters, this is in Django 1.5.

My question is: What do I need to do in order to display the inline LogOutput on the DataFile's admin page? (Or is my thinking about the use of a OneToOneField in need of revision?)

TIA!

Community
  • 1
  • 1
AC Capehart
  • 1,260
  • 10
  • 12
  • Have you tried the fk_name? fk_name = "logoutput" – petkostas Jan 02 '14 at 19:16
  • I am unfamiliar with it. I'll have a look. – AC Capehart Jan 02 '14 at 20:03
  • It appears to be the same problem. Specifically that the directionality is still presumed to be the other way: LogOutputInline.fk_name' refers to field '' that is missing from model 'trial.LogOutput – AC Capehart Jan 02 '14 at 20:16
  • Seems its correct behavior: https://code.djangoproject.com/ticket/18059 – petkostas Jan 02 '14 at 20:41
  • possible duplicate of [Django Admin: OneToOne Relation as an Inline?](http://stackoverflow.com/questions/1744203/django-admin-onetoone-relation-as-an-inline) – Anto Mar 26 '14 at 13:56
  • 1
    @Anto - The question itself already refers to [Django Admin: OneToOne Relation as an Inline?](http://stackoverflow.com/questions/1744203/django-admin-onetoone-relation-as-an-inline) as a possible duplicate AND why it's NOT applicable. – AC Capehart Mar 26 '14 at 20:17

2 Answers2

5

There's also a django add-on to get inlining without having to reverse the direction of the OneToOneField(s): django_reverse_admin

You'll need to add django_reverse_admin to your requirements.txt:

-e git+https://github.com/anziem/django_reverse_admin.git#egg=django_reverse_admin

Then import it:

admin.py

from django_reverse_admin import ReverseModelAdmin

# don't need to define an inline for LogOutput

class DataFileAdmin(ReverseModelAdmin):
    search_fields = ['name']
    inline_reverse = ['logoutput']
    inline_type = 'tabular'  # or could be 'stacked'

admin.site.register(DataFile, DataFileAdmin)
lynx
  • 310
  • 4
  • 7
3

The internal ORM and admin are pretty smart, but where OOP runs up against table structure, things can be a bit imprecise. I'd recommend giving the ORM a little hint by using an abstract base class like this:

class LogOutput(models.Model):
    raw = models.TextField()

    class Meta:
        abstract = True

class DataFileLogOutput(LogOutput):
    pass

class SuiteFileLogOutput(LogOutput):
    pass

class DataFile(models.Model):
    name = models.CharField()#etc.
    logoutput = models.OneToOneField(DataFileLogOutput)

class Suite(models.Model):
    # Many Suites will have the same datafile:
    datafile = models.ForeignKey(DataFile)

    # Each Suite has a unique LogOutput different from the DataFile's
    # and as with the DataFile, that LogOutput will have just one Suite
    logoutput = models.OneToOneField(SuiteLogOutput)
  • Yeah, I was hoping to avoid that, but I can see how my mental model of it might not line up with how the ORM manages the SQL. Thanks for the pointer! – AC Capehart Jan 02 '14 at 21:48
  • 1
    How does this allow the reversing of the logoutput OneToOne relation in the admin? (regarding the directionality of how admin resolves relations) – bryanph Nov 04 '15 at 09:31
  • This is not possible when havong auth.User (django's system user) as Model. I can't change that (without much hassle). – nerdoc Oct 26 '21 at 21:09