Thursday, March 30, 2017

Django ORM: Override related_name of Field in Child Class

Leave a Comment

I get this exception:

django.core.exceptions.FieldError:

Local field 'ticket' in class 'SpecialPlugin' clashes with field of similar name from base class 'BasePlugin'

Here are my models:

class BasePlugin(models.Model):     ticket = models.OneToOneField('foobar.ticket', primary_key=True,                                    related_name='%(app_label)s_%(class)s')      class Meta(IndexImplementation.Meta):         abstract = True      # .. Other stuff which should be available for SpecialPlugin      #    and other child classes.  class SpecialPlugin(BasePlugin):     ticket = models.OneToOneField('foobar.ticket', primary_key=True,                                    related_name='special') 

I only found this note, but in my case the parent class is abstract. I am unsure if it applies here.

I want to give the child class SpecialPlugin the related name "special" since the related name (%(app_label)s_%(class)s) of the BasePlugin would break old code.

Is there a way to give SpecialPlugin.ticket the related_name "special"?

2 Answers

Answers 1

It might look like an ugly hack, but you can set a function call to the related_name argument instead of string. And then override that function in the child class/model.

class BasePlugin(models.Model):      @staticmethod     def get_ticket_related_name():         return '%(app_label)s_%(class)s'      ticket = models.OneToOneField('foobar.ticket', primary_key=True,                                    related_name=get_ticket_related_name.__func__())      class Meta(IndexImplementation.Meta):         abstract = True   class SpecialPlugin(BasePlugin):     @staticmethod     def get_ticket_related_name():         return 'special' 

Answers 2

It looks like the core of the problem is in the overriding of model field Django model inheritance, overriding fields

Simple workaround for you problem will be to decouple BasePlugin to to class without ticket field and then create a child class that contains ticket field

class BaseWithoutTicketPlugin(models.Model):     # .. Other stuff which should be available for SpecialPlugin      #    and other child classes.     class Meta(IndexImplementation.Meta):         abstract = True  class BasePlugin(BaseWithoutTicketPlugin):     ticket = models.OneToOneField('foobar.ticket', primary_key=True,                                    related_name='%(app_label)s_%(class)s')      class Meta(BaseWithoutTicketPlugin.Meta):         abstract = True   class SpecialPlugin(BaseWithoutTicketPlugin):     ticket = models.OneToOneField('foobar.ticket', primary_key=True,                                    related_name='special') 

Idea is to use BaseWithoutTicketPlugin when you need to customize ticket and use BasePlugin when you don't.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment