Search code examples
pythondjangomodels

How to store functions in django models


edit: I completely rewrote the question as the original one didn't clearly explain my question

I want to run a function which is specific to each particular model instance.

Ideally I want something like this:

class MyModel(models.Model):
    data = models.CharField(max_length=100)
    perform_unique_action = models.FunctionField() #stores a function specific to this instance

x = MyModel(data='originalx', perform_unique_action=func_for_x)
x.perform_unique_action() #will do whatever is specified for instance x

y = MyModel(data='originaly', perform_unique_action=func_for_y)
y.perform_unique_action() #will do whatever is specified for instance y

However there is no datatype FunctionField. Normally this would be solvable with inheritance, and creating subclasses of MyModel, maybe like this:

class MyModel(models.Model):
    data = models.CharField(max_length=100)
    perform_unique_action = default_function

class MyModelX(MyModel):
    perform_unique_action = function_X

class MyModelY(MyModel):
    perform_unique_action = function_Y

x = MyModelX(data='originalx')
x.perform_unique_action() #will do whatever is specified for instance x

y = MyModelY(data='originaly')
y.perform_unique_action() #will do whatever is specified for instance y

Unfortunately, I don't think I can use inheritance because I am trying to access the function this way:

class MyModel(models.Model):
    data = models.CharField(max_length=100)
    perform_unique_action = default_function

class SecondModel(models.Model):
    other_data = models.IntegerField()
    mymodel = models.ForeignKey(MyModel)

secondmodel = SecondModel.objects.get(other_data=3)
secondmodel.mymodel.perform_unique_action()

The problem seems to be that I don't know what type the foreign key is going to be in SecondModel if I override the perform_unique_action in subclasses.

Can I access MyModel from SecondModel as a foreign key and still have a unique function for each instance of MyModel?


Solution

  • This works for me. I haven't tested it, but you should be able to create another class and override their methods and it'll work. Check the class Meta line, it'll treat it as an abstract class. Here's an example of my actual classes that I'm working on right now.

    EDIT: Added VoteComment class and tested it. It works as expected!

    class Vote(models.Model):
        VOTE_ENUM = (
            (VoteEnum.DOWN_VOTE, VoteEnum.toString(VoteEnum.DOWN_VOTE)),
            (VoteEnum.NONE, VoteEnum.toString(VoteEnum.NONE)),
            (VoteEnum.UP_VOTE, VoteEnum.toString(VoteEnum.UP_VOTE)),
    
        )
        question = models.ForeignKey(Question, null=False, editable=False, blank=False)
        voter = models.ForeignKey(User, blank=False, null=False, editable=False)
        vote_type = models.SmallIntegerField(default=0, null=False, blank=False, choices=VOTE_ENUM)
    
        class Meta:
            abstract = True
    
        def is_upvote(self):
            return self.vote_type > 0
        def is_downvote(self):
            return self.vote_type < 0
    
    class VoteAnswer(Vote):
        answer = models.ForeignKey(Answer, null=False, editable=False, blank=False)
    
        class Meta:
            unique_together = (("voter", "answer"),) # to prevent user from voting on the same question/answer/comment again
    
        def __unicode__(self):
            vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
            return u"{0}: [{1}] {2}".format(user.username, vote_type, answer.text[:32])
    
        def is_upvote(self):
            return "FOO! "+str(super(VoteAnswer, self).is_upvote())
    
    class VoteComment(Vote):
        comment = models.ForeignKey(Comment, null=False, editable=False, blank=False)
    
        class Meta:
            unique_together = (("voter", "comment"),) # to prevent user from voting on the same question/answer/comment again
    
        def __unicode__(self):
            vote_type = "UP" if vote_type > 0 else ("DOWN" if vote_type < 0 else "NONE")
            return u"{0}: [{1}] {2}".format(user.username, vote_type, comment.text[:32])
    
        def is_upvote(self):
            return "BAR!"