eugene
eugene

Reputation: 41725

Django, treat OneToOne related field as my own field

I'm essentially trying to come up with my own inheritance scheme because Django's inheritance doesn't fit my needs.

I'd like parent table(class) hold common data fields.
sub classess would have its own additional data in a separate table.

class ProductBase(models.Model):

    common = models.IntegerField()

    def get_price(self):
        return some_price


class FooProduct(ProductBase):

    # no field because I'm proxy

    class Meta:
        proxy = True

    def get_price(self):
        return price_using_different_logic


class FooExtra(models.Model):

    base = models.OneToOneField(ProductBase, primary_key=True)
    phone = models.CharField(max_length=10)

My question is, would it be able to treat as if Foo has FooExtra's fields?

I'd like to do things like following..

foo = FooProduct.objects.create()

foo.phone = "3333"  # as django does with its multiple inheritance
foo.save()
FooProduct.objects.filter(phone="3333")  

I'd like to list Products of different kind(data)

  1. I need to list them together, so abstract Base inheritance is out

  2. from the list, I'd like to treat each model as polymorphic model, when iterating over ProductBase.objects.all(), product.get_price() will use appropriate classe's method. (without incurring join if don't have to)

  3. When and only when I want, I retrieve the addtional table data (by something like .select_related('fooextra')

Django-polymorphic is close to what I want, but it is rather obscure what it does so I'm afraid to use it, and I think it fails #3.

Upvotes: 0

Views: 580

Answers (2)

mastazi
mastazi

Reputation: 1672

If I understand well, you want inheritance and you want the fields that are specific to the child class to be on a separate table. As far as I know, you don't need a proxy class to achieve that, you could just implement multi-table inheritance as specified in the manual at https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance e.g.:

class Base(models.Model):
    common = models.IntegerField()


class Foo(Base):
    phone = models.CharField(max_length=10)

This, as explained at the link above, will automatically create a one-to-one relationship. And of course you can do foo.phone = "3333" (where foo is of type Foo) as in your example above. And the neat thing is that you can also access foo.common whereas in your example it would have been foo.base.common.

Upvotes: 2

Ben
Ben

Reputation: 6777

It doesn't seem like you want anything different to Django's standard inheritance.

class ProductBase(models.Model):
    common1 = models.IntegerField()
    common2 = models.IntegerField()

class FooProduct(ProductBase):
    fooextra = models.IntegerField()

class BarProduct(ProductBase):
    barextra = models.IntegerField()

If you create instances of each:

foo1 = FooProduct(common1=1, common2=1, fooextra=1)
foo2 = FooProduct(common1=1, common2=1, fooextra=2)
bar1 = BarProduct(common1=1, common2=1, barextra=1)
bar2 = BarProduct(common1=1, common2=1, barextra=2)

You can loop over all products:

for product in ProductBase.objects.all():
    print product.common1, product.common2

From a ProductBase object that is actually a FooProduct, you can get the custom field with:

product.foo.fooextra

From a ProductBase object that is actually a BarProduct, you can get the custom field with:

product.bar.barextra

You can still do querying:

foo = FooProduct.objects.get(fooextra=1)
bar = BarProduct.objects.get(barextra=2)

And you can access the common fields directly on those objects:

foo.common1
bar.common2 

You can use the InheritanceManager from django-model-utils if you need more control over querying etc - and this should address point 3, too: ProductBase.objects.filter(...).select_subclasses() would give you the FooProduct and BarProduct objects instead of ProductBase objects.

Upvotes: 1

Related Questions