[Fixed]-Child object by Parent

1👍

The Interaction objects will either have a postinteraction or newsinteraction attribute that will point to the object of the right type.

Alternatively you can use a library like django-model-utils and its InheritanceManager to allow you to do:

from model_utils.managers import InheritanceManager

class Interaction(models.Model):
    ...
    objects = InheritanceManager()
    ...

Interaction.objects.all().select_subclasses()

That will give you the right object types.

👤Ben

0👍

You asked how to tell the subclass of an Interaction object without doing a query. The short answer is that you can’t. When you access Interaction.objects.all(), that just queries the myschema_interaction table, and that table doesn’t tell you whether there’s a matching record in the myschema_postinteraction table or the myschema_newsinteraction table.

To see what the subclass is, you can look at the postinteraction or newsinteraction attributes. To make one query instead of making a separate query for each record, you can use the prefetch_related() method.

Here’s a runnable example that shows the two techniques.

# Tested with Django 1.9.2
import logging
import sys

import django
from django.apps import apps
from django.apps.config import AppConfig
from django.conf import settings
from django.db import connections, models, DEFAULT_DB_ALIAS
from django.db.models.base import ModelBase

NAME = 'udjango'


def main():
    setup()

    class Interaction(models.Model):
        # user = models.ForeignKey(
        #     User,
        #     verbose_name="Usuario"
        # )

        created = models.DateTimeField(
            verbose_name="Criado em",
            auto_now_add=True
        )

        value = models.FloatField(
            verbose_name="Valor",
            default=0
        )

    class PostInteraction(Interaction):
        body = models.CharField(max_length=1000)

    class NewsInteraction(Interaction):
        headline = models.CharField(max_length=30)

    syncdb(Interaction)
    syncdb(PostInteraction)
    syncdb(NewsInteraction)

    PostInteraction.objects.create(body="I ate breakfast.", value=1)
    NewsInteraction.objects.create(headline="No lunch today!", value=2)
    PostInteraction.objects.create(body="I ate dinner.", value=3)

    logging.info('All interaction values: {}'.format(
        [interaction.value
         for interaction in Interaction.objects.all()]))
    logging.info('Interaction values and posts: {}'.format(
        [(interaction.value, hasattr(interaction, 'postinteraction'))
         for interaction in Interaction.objects.all()]))
    logging.info('Interaction values and posts: {}'.format(
        [(interaction.value, hasattr(interaction, 'postinteraction'))
         for interaction in Interaction.objects.prefetch_related('postinteraction')]))


def setup():
    DB_FILE = NAME + '.db'
    with open(DB_FILE, 'w'):
        pass  # wipe the database
    settings.configure(
        DEBUG=True,
        DATABASES={
            DEFAULT_DB_ALIAS: {
                'ENGINE': 'django.db.backends.sqlite3',
                'NAME': DB_FILE}},
        LOGGING={'version': 1,
                 'disable_existing_loggers': False,
                 'formatters': {
                    'debug': {
                        'format': '%(asctime)s[%(levelname)s]'
                                  '%(name)s.%(funcName)s(): %(message)s',
                        'datefmt': '%Y-%m-%d %H:%M:%S'}},
                 'handlers': {
                    'console': {
                        'level': 'DEBUG',
                        'class': 'logging.StreamHandler',
                        'formatter': 'debug'}},
                 'root': {
                    'handlers': ['console'],
                    'level': 'INFO'},
                 'loggers': {
                    "django.db": {"level": "DEBUG"}}})
    app_config = AppConfig(NAME, sys.modules['__main__'])
    apps.populate([app_config])
    django.setup()
    original_new_func = ModelBase.__new__

    @staticmethod
    def patched_new(cls, name, bases, attrs):
        if 'Meta' not in attrs:
            class Meta:
                app_label = NAME
            attrs['Meta'] = Meta
        return original_new_func(cls, name, bases, attrs)
    ModelBase.__new__ = patched_new


def syncdb(model):
    """ Standard syncdb expects models to be in reliable locations.

    Based on https://github.com/django/django/blob/1.9.3
    /django/core/management/commands/migrate.py#L285
    """
    connection = connections[DEFAULT_DB_ALIAS]
    with connection.schema_editor() as editor:
        editor.create_model(model)

main()

Here’s the log output that includes the executed SQL:

2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.schema.execute(): CREATE TABLE "udjango_interaction" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "created" datetime NOT NULL, "value" real NOT NULL); (params None)
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) CREATE TABLE "udjango_interaction" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "created" datetime NOT NULL, "value" real NOT NULL); args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.schema.execute(): CREATE TABLE "udjango_postinteraction" ("interaction_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "udjango_interaction" ("id"), "body" varchar(1000) NOT NULL); (params None)
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) CREATE TABLE "udjango_postinteraction" ("interaction_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "udjango_interaction" ("id"), "body" varchar(1000) NOT NULL); args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.schema.execute(): CREATE TABLE "udjango_newsinteraction" ("interaction_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "udjango_interaction" ("id"), "headline" varchar(30) NOT NULL); (params None)
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) CREATE TABLE "udjango_newsinteraction" ("interaction_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "udjango_interaction" ("id"), "headline" varchar(30) NOT NULL); args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) PRAGMA foreign_keys = 0; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_interaction" ("created", "value") VALUES ('2017-04-27 15:02:20.729660', 1.0); args=['2017-04-27 15:02:20.729660', 1.0]
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_postinteraction" ("interaction_ptr_id", "body") SELECT 1, 'I ate breakfast.'; args=(1, 'I ate breakfast.')
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_interaction" ("created", "value") VALUES ('2017-04-27 15:02:20.754687', 2.0); args=['2017-04-27 15:02:20.754687', 2.0]
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_newsinteraction" ("interaction_ptr_id", "headline") SELECT 2, 'No lunch today!'; args=(2, 'No lunch today!')
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) BEGIN; args=None
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_interaction" ("created", "value") VALUES ('2017-04-27 15:02:20.778166', 3.0); args=['2017-04-27 15:02:20.778166', 3.0]
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) INSERT INTO "udjango_postinteraction" ("interaction_ptr_id", "body") SELECT 3, 'I ate dinner.'; args=(3, 'I ate dinner.')
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value" FROM "udjango_interaction"; args=()
2017-04-27 15:02:20[INFO]root.main(): All interaction values: [1.0, 2.0, 3.0]
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value" FROM "udjango_interaction"; args=()
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value", "udjango_postinteraction"."interaction_ptr_id", "udjango_postinteraction"."body" FROM "udjango_postinteraction" INNER JOIN "udjango_interaction" ON ("udjango_postinteraction"."interaction_ptr_id" = "udjango_interaction"."id") WHERE "udjango_postinteraction"."interaction_ptr_id" = 1; args=(1,)
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value", "udjango_postinteraction"."interaction_ptr_id", "udjango_postinteraction"."body" FROM "udjango_postinteraction" INNER JOIN "udjango_interaction" ON ("udjango_postinteraction"."interaction_ptr_id" = "udjango_interaction"."id") WHERE "udjango_postinteraction"."interaction_ptr_id" = 2; args=(2,)
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value", "udjango_postinteraction"."interaction_ptr_id", "udjango_postinteraction"."body" FROM "udjango_postinteraction" INNER JOIN "udjango_interaction" ON ("udjango_postinteraction"."interaction_ptr_id" = "udjango_interaction"."id") WHERE "udjango_postinteraction"."interaction_ptr_id" = 3; args=(3,)
2017-04-27 15:02:20[INFO]root.main(): Interaction values and posts: [(1.0, True), (2.0, False), (3.0, True)]
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value" FROM "udjango_interaction"; args=()
2017-04-27 15:02:20[DEBUG]django.db.backends.execute(): (0.000) SELECT "udjango_interaction"."id", "udjango_interaction"."created", "udjango_interaction"."value", "udjango_postinteraction"."interaction_ptr_id", "udjango_postinteraction"."body" FROM "udjango_postinteraction" INNER JOIN "udjango_interaction" ON ("udjango_postinteraction"."interaction_ptr_id" = "udjango_interaction"."id") WHERE "udjango_postinteraction"."interaction_ptr_id" IN (1, 2, 3); args=(1, 2, 3)
2017-04-27 15:02:20[INFO]root.main(): Interaction values and posts: [(1.0, True), (2.0, False), (3.0, True)]

Leave a comment