【问题标题】:How to store functions in django models如何在 django 模型中存储函数
【发布时间】:2012-06-20 13:25:14
【问题描述】:

编辑:我完全重写了这个问题,因为原来的问题没有清楚地解释我的问题

我想运行一个特定于每个特定模型实例的函数。

理想情况下,我想要这样的东西:

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

但是没有数据类型 FunctionField。通常这可以通过继承和创建 MyModel 的子类来解决,可能是这样的:

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

不幸的是,我认为我不能使用继承,因为我试图以这种方式访问​​该函数:

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()

问题似乎是,如果我覆盖子类中的 perform_unique_action,我不知道 SecondModel 中的外键是什么类型。

我可以从 SecondModel 作为外键访问 MyModel 并且对 MyModel 的每个实例仍然具有唯一的功能吗?

【问题讨论】:

  • 我真的不明白你在做什么。但对我来说,看起来你可以在你的信号方法中实现同样的事情,而不需要整个“模型中的函数”方法。你能试着多描述一下你的问题吗?尤其是最后一段很难理解。
  • 更新了最后一段。如果你能提供一个类似于上面的例子,说明如何在信号方法中实现同样的事情,而不需要序列化函数,那就太好了。
  • 除了您认为您要运行的方法必须在模型中之外,还有其他好的理由吗?如果不是,为什么不在保存扩展对象时(通过信号)运行该方法?您可以从信号方法中获取几乎所有可能需要的数据,因为实际实例已传递给该方法。
  • 我再次阅读了您的帖子并有了另一个想法。您想使用与扩展不同的功能来“专门化”您的 Extension 模型。听起来您应该在模型中使用继承。定义一个基本模型Extension,然后通过创建另一个继承基本模型并覆盖该方法的模型来定义该模型的专用版本。由于您想分离关注点,您还应该将这些单独的扩展拆分为几个 Django 应用程序。

标签: python django models


【解决方案1】:

这对我有用。我还没有测试过它,但你应该能够创建另一个类并覆盖它们的方法,它会工作的。检查class Meta 行,它会将其视为抽象类。这是我现在正在学习的实际课程的示例。

编辑:添加了 VoteComment 类并对其进行了测试。它按预期工作!

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

【讨论】:

  • 我能用不同的 is_upvote 函数创建 VoteAnswer 对象吗?如果可以,请提供一个示例,因为我不确定如何实现。请参阅我上面的最后一个带有外键的代码示例,了解为什么我认为子类不适合我的特定问题。
  • 我刚刚进行了快速测试并添加了 VoteComment() 类。它按预期工作。 VoteAnswer 打印“FOO!False”,VoteComment 打印“BAR!”。
  • 但是每个 VoteAnswer 对象都会打印 FOO 并且每个 VoteComment 都会打印 BAR。我正在寻找五个具有不同功能的相同对象。因此,对于您的示例,我希望这样做:vote_answer_obj.answer.some_func() 其中 some_func 将根据 vote_answer_obj 中提到的答案实例而有所不同。到目前为止,这些示例只为每个类提供了独特的功能,而不是为每个对象提供了独特的功能。编辑:我意识到我通过将函数分配给属性而不是方法来打破 OOP 范式,但是对于我的示例,我看不到解决方法。
  • 是的,这不是 OOP 的工作方式。一个类的所有对象都将使用相同的功能。但是,我认为您可能可以使用函数指针。也许有一个类有一个func_ptr 成员变量,然后定义set_func_ptr(myFunc) 方法将您的自定义函数分配给func_ptr 变量。这可能是您的解决方法。您必须在类之外的某个地方定义自定义函数。
  • 我正在考虑使用 marshal,这是一种将函数转换为序列化形式的方法,然后可以将其存储在数据库中……但这似乎是错误的做事方式,并且可能容易出现安全漏洞。非常感谢您帮助我更好地了解我的情况和潜在的解决方案。
【解决方案2】:

我想出了两种方法来为每个对象定义一个特定的函数。一种是使用 marshal 创建可以存储在数据库中的字节码(不是一个好方法),另一种是通过存储对要运行的函数的引用,正如 Randall 所建议的那样。这是我使用存储引用的解决方案:

class MyModel(models.Model):
    data = models.CharField(max_length=100)
    action_module = models.CharField(max_length=100)
    action_function = models.CharField(max_length=100)

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

secondmodel_obj = SecondModel.objects.get(other_data=3)
#The goal is to run a function specific to the instance
#of MyModel referred to in secondmodel_obj

module_name = secondmodel_obj.mymodel.action_module
func_name = secondmodel_obj.mymodel.action_function

module = __import__(module_name)
func = vars(module)[func_name]
func()

感谢所有回复的人,如果不是你的帮助,我不可能得到这个答案。

【讨论】:

    【解决方案3】:

    您可以实现一些类似的行为来覆盖保存方法。并为您的实例提供特殊回调。

    类似:

    def default_function(instance):
        #do something with the model instance
    
    class ParentModel(model.Model):
        data = models.CharField()
        callback_function = default_function
    
        def save(self, *args, **kwargs):
            if hasattr(self, 'callback_function'):
                self.callback_function(self)
            super(ParentModel, self).save(*args, **kwargs)
    
    class ChildModel():
        different_data = models.CharField()
        callback_function = other_fun_specific_to_this_model
    
    instance = ChildModel()
    #Specific function to this particular instance
    instance.callback_function = lambda inst: print inst.different_data
    instance.save()
    

    【讨论】:

    • 这看起来像我需要的。但是,回调函数是否存储在数据库中?例如,当我保存一个实例时,该实例超出范围,然后我稍后再次从数据库中获取该实例,它是否保留 callback_function?
    • 好吧,我有一个快速破解,我明白为什么你有子模型。我只用父函数加上一个'change_func'函数来尝试它,这样我就可以设置父函数。但是当模型从数据库中重新获取时,它会重置为 default_function。孩子会阻止这种情况。感谢您对此的帮助。仍然不是我想要的,我会尝试编辑和改写问题。
    【解决方案4】:

    您可以在您的服务器上编写端点,并将它们的访问权限限制为您自己。然后在每个模型实例中存储对应的url。例如:

    views.py

    def funx_x(request):
        pass
    
    def func_y(request):
        pass
    

    models.py:

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

    然后:

    x = MyModel(data='originalx', perform_unique_action='http://localhost/funx_x')
    requests.post(x.perform_unique_action)
    

    【讨论】:

      【解决方案5】:

      我不知道我理解你是否正确。但是你可以查看这个例子here

      例子:

      表示模型属性的字符串。这与可调用的行为几乎相同,但在这种情况下 self 是模型实例。这是一个完整的模型示例:

      class Person(models.Model):
          name = models.CharField(max_length=50)
          birthday = models.DateField()
      
          def decade_born_in(self):
              return self.birthday.strftime('%Y')[:3] + "0's"
          decade_born_in.short_description = 'Birth decade'
      
      class PersonAdmin(admin.ModelAdmin):
          list_display = ('name', 'decade_born_in')
      

      【讨论】:

      • 这将为每个人运行相同的函数 -decade_born_in。我想为每个不同的 Person 实例运行不同的函数。
      猜你喜欢
      • 2019-08-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-08-16
      • 2016-02-11
      • 2011-05-16
      • 2019-10-22
      • 1970-01-01
      相关资源
      最近更新 更多