Note that there are some explanatory texts on larger screens.

plurals
  1. POUsing a Proxy Model when accessing foreign key
    primarykey
    data
    text
    <p>I have two related Django models. One of the models does expensive calculations in its <code>__init__</code> which I can't move elsewhere without unacceptable cost/risk. </p> <p>It turns out these expensive calculations aren't needed in all contexts, so I introduced a proxy model that circumvents them. They are needed more often than not, though, so it's not practical to make the expensive one into the proxy.</p> <p>So, my code basically looks like this:</p> <pre><code>class Person(models.Model): def __init__(self, *args, **kw): models.Model.__init__(self, *args, **kw) do_some_really_expensive_things() class LightweightPerson(Person): class Meta: proxy = True def __init__(self, *args, **kw): models.Model.__init__(self, *args, **kw) class PersonFact(models.Model): fact = models.TextField() person = models.ForeignKey(Person) </code></pre> <p>This works well -- most of my code queries on <code>Person</code>. And in the few places where the code doesn't need the really expensive things, it queries on <code>LightweightPerson</code> instead, and performs better.</p> <p>However, some of my code starts from <code>PersonFact</code> instances and accesses the related <code>person</code> for each <code>PersonFact</code>. This code doesn't need the really expensive person calculations, and the performance hit from those expensive calculations is unacceptable. So I'd like to be able to instantiate a <code>LightweightPerson</code> instead of a <code>Person</code> in this context.</p> <p>The approach I came up with was to add a second <code>ForeignKey</code> that references the proxy class, and uses the same database column:</p> <pre><code>class PersonFact(models.Model): fact = models.TextField() person = models.ForeignKey(Person, db_column="person_id") lightweight_person = models.ForeignKey( LightweightPerson, db_column="person_id", related_name="lightweight_personfact_set") </code></pre> <p>So now when I need the performance boost, my code can do things like this:</p> <pre><code>facts = PersonFact.objects.select_related( "lightweight_person").all() for fact in facts: do_something_with(fact.lightweight_person) </code></pre> <p>And this works great! Until I try to save a new <code>PersonFact</code>:</p> <pre><code>&gt;&gt;&gt; fact = PersonFact(fact="I like cheese", person=some_guy_i_know) &gt;&gt;&gt; fact.save() Traceback (most recent call last): ... DatabaseError: column "person_id" specified more than once </code></pre> <p>:-(</p> <p>Is there any way to do this without a big scary refactoring of the code that's currently in <code>Person.__init__</code>? Ideally I'd either be able to just signal "when accessing <code>person_fact.person</code> right now, please instantiate a <code>LightweightPerson</code> instead of a <code>Person</code>" in my calling code. Or, alternatively, I'd like to be able to declare a "proxy related field" on <code>PersonFact</code> that shadows the same database column, but Django's internals know to only interact once with the database column.</p>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload