Admin presentation for different types of the same model or is the Inheritance needed ?

Hi there,
I have very very simple store with only AudioProduct. The model (simplified version) is here:

class AudioProduct(models.Model):
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    audio_path = models.FileField(upload_to='audiofiles/', blank=True, null=True)
    duration = models.IntegerField()

Now I have to extend this “store” to cover also PaintingProduct (I know, I was naive to trust the user that only audio will be used forever and ever).
When speaking of ORM, I’m very afraid of using model inheritance so I prefer to use type field and put everything in one DB table with different fields NULLABLE.

So my idea is to have this:

PRODUCT_TYPES = (
    ('A', 'Audio'),
    ('P', 'Painting'),
)

class Product(models.Model):
    product_type = models.CharField(max_length=1, choices=PRODUCT_TYPES)
    name = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True)
    # AUDIO specific attributes
    price = models.DecimalField(max_digits=10, decimal_places=2,blank=True, null=True)
    audio_path = models.FileField(upload_to='audiofiles/', blank=True, null=True)
    # PAINTING specific attributes
    painting_technique = models.CharField(max_length=50,blank=True, null=True)
    thumbnail_image = models.FileField(upload_to='imagefiles/', blank=True, null=True)

But here comes the problem - I kind of need to separate the Admin presentation for Audio and Painting - or at least I need 2 different ADD and CHANGE pages based on the Product type ?
For example, I want to have Add Audio Product and Add Painting Product pages separetely to show only the needed fields.
Is that possible to achieve or do I need to go with inheritance ?

Thank you.
Hrvoje

This can be done, but you might run into some odd behavior.

You could create Proxy models, one for each of your audio and painting types.

Register each of them in the admin with a custom admin class only showing the required fields for that type.

Do not show the product_type field to prevent confusion.

Override the get_queryset in the ModelAdmin class to only return instances of that type.

Override the save methods on the proxy models to set the product_type on an Add. (You may also want to have it null out the fields for the other type.)

(I think that covers most of it - there might be more that you would want to do depending upon your specific needs. Note, I am suggesting adding two proxy models in addition to having an interface for the Product model directly to ensure you still have some facility to clean up any models that might have gotten messed-up - having data in both sections.)

Ken

1 Like

I registered ProductAudioAdmin and ProductPaintingAdmin and as a result I had 2 “sections” in Django Admin - one is for viewing/editing ProductPainting and one for viewing/editing ProductAudio.

Now I also registered parent ProductAdmin and in this page I see all products. But when I want to edit (or add) product, then all fields are shown. Is it possible that Django Admin sees the difference between existing products and show only needed fields when I want to edit product ?

class ProductAdmin(admin.ModelAdmin):
    # ...

class ProductAudioAdmin(ProductAdmin):
    fields = ['name','audio_duration_minutes']
    class Meta:
        proxy = True

class ProductPaintingAdmin(ProductAdmin):
    fields = ['name','painting_technique','painting_size']
    class Meta:
        proxy = True

Ok, I used get_fields to control which fields to show on CHANGE page:

class ProductAdmin(admin.ModelAdmin):
    readonly_fields = ['created_at','updated_at']
    def get_fields(self, request, obj=None):
        if obj:
            if obj.product_type == 'a':
                return [...]
            if obj.product_type == 'p':
                return [...]

This works perfect for changing existing products.

Now I have to find a way to have different ADD actions (it would be perfect to have 2 buttons ADD Audio Product and ADD Painting Product and then pre-define product_type based on which button was clicked).

Extracted from the Django documentation for the admin site:

The admin’s recommended use is limited to an organization’s internal management tool. It’s not intended for building your entire front end around.

If you need to provide a more process-centric interface that abstracts away the implementation details of database tables and fields, then it’s probably time to write your own views.

(Emphasis added)

You may still be fine now - but as you keep adding more type-specific features to your process, you’re creating more work for yourself. You will eventually reach a point where you’re expending more effort making things work in the admin than if you just created your own views.

Now, aside from that, you do have different add actions. You can select add from the model type chosen. You can then add a custom save_model method in your ModelAdmin class to set the product_type.

@KenWhitesell thank you. All you said is true. But for I now I still find Django Admin feasible :slight_smile:. Yes, there are sometimes tricky parts but somehow there is always a workaround which I can agree to at the end - as it is said Django is for developers with deadlines.

In this specific case at the end I decided to have 2 steps. In first step you decide for the product_type and then in the next step (when you edit the article) the additional fields will be shown based on product_type which works quite nice.

So I added else part to get_fields()

    def get_fields(self, request, obj=None):
        if obj:
            if obj.product_type == 'a':
                return [...]
            if obj.product_type == 'p':
                return [...]
        else: # in case of creating new article
            return ['title','template_type', ... ]