Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Inheritance and factory functions in Python and Django

I'm creating a Django app that uses some inheritance in it's model, mainly because I need to assign everything a UUID and a reference so I know what class it was. Here's a simplified version of the base class:

class BaseElement(models.Model):
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
    objmodule = models.CharField(max_length=255, editable=False, blank=False)
    objclass = models.CharField(max_length=255, editable=False, blank=False)

class ChildElement(BaseElement):
    somefield = models.CharField(max_length=255)

I'd like to make sure that objmodule, objclass, and uuid are set automatically. I've learned from this post that it's a bad idea to do that by writing my own constructor, and that I'm better off writing a factory function. So now my BaseElement and ChildElement looks like this:

class BaseElement(models.Model):
    uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
    objmodule = models.CharField(max_length=255, editable=False, blank=False)
    objclass = models.CharField(max_length=255, editable=False, blank=False)

    def set_defaults(self):
        self.objmodule = unicode(self.__class__.__module__)
        self.objclass = unicode(self.__class__.__name__)
        self.uuid = unicode(uuid4())

class ChildElement(BaseElement):
    somefield = models.CharField(max_length=255)

    @staticmethod
    def create(*args, **kwargs):
        ce = ChildElement(*args, **kwargs)
        ce.set_defaults()
        return ce

This works. I can call ChildElement.create(somefield="foo") and I will get an appropriate object with uuid, objmodule, and objclass fields set correct. However, as I go through and create more classes like ChildElement2 and ChildElement3, I'm finding that I'm inserting the exact same static factory function. This grates on me because code duplication is bad.

With normal methods I'd just insert the create factory function in BaseElement, however, I can't do that here because I don't have a handle to self (because it hasn't been created yet) to get information about the class of the object that invoked the method.

Is there a way that I can migrate this factory into the BaseElement class so I don't have to duplicate this code everywhere and still have it so it automatically sets the values of uuid, objmodule, and objclass?

like image 235
Pridkett Avatar asked Dec 11 '09 21:12

Pridkett


People also ask

What are factory functions in Python?

Factory method is a creational design pattern which solves the problem of creating product objects without specifying their concrete classes. The Factory Method defines a method, which should be used for creating objects instead of using a direct constructor call ( new operator).

What is the use of factory functions?

Factory functions are mainly used when the user wants to initialize the object of a class multiple times with some assigned value or static values. It makes the process easy since we just have to call this function and retrieve the new object created.

What is a factory class in Python?

A Class Factory is a function that creates and returns a class. It is one of the powerful patterns in Python.

What is DjangoModelFactory?

DjangoModelFactory is a basic interface from factory_boy that gives "ORM powers" to your factories. It's main feature here is that it provides you with a common "create" and "build" strategies that you can use to generate objects in your tests.


1 Answers

If you make create() a @classmethod instead of @staticmethod, you'll have access to the class object, which you can use instead of referring to it by name:

@classmethod
def create(cls, *args, **kwargs):
    obj = cls(*args, **kwargs)
    obj.set_defaults()
    return obj

This is now generic and can go on the base class instead of each child class.

like image 198
Carl Meyer Avatar answered Sep 22 '22 01:09

Carl Meyer