Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django ORM: Override related_name of Field in Child Class

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"?

like image 914
guettli Avatar asked Mar 20 '17 09:03

guettli


2 Answers

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.

like image 188
Sardorbek Imomaliev Avatar answered Oct 17 '22 09:10

Sardorbek Imomaliev


Update:

Similar thing can be done using django's default_related_name meta option.


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'
like image 37
v1k45 Avatar answered Oct 17 '22 10:10

v1k45