如何让用户在Django中编辑/更新.py文件?

2024-05-13 18:57:39 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在处理一个Django项目,我希望用户能够在.py文件中添加代码。嗯,我有.py文件,其中包含一些代码,在代码中间我有一些代码行,这些代码为模糊逻辑算法指定了一些规则,如下所示:

 ...
rule1 = scikit-fuzzy.control.Rule(input1["bad"] & input2["bad"] & input3["bad"] & input4["bad"] & input5["average"], output["poor"])
 ...

因此,我总共有243行代码(换句话说,就是规则),就像上面的代码一样,集中在一些代码之间(在它上面,下面是一些其他代码)。现在,我希望管理员能够编辑和更新这些代码行,通过使用表单,我将从管理员那里获取输入(例如:“好”、“坏”、“平均”、“坏”、“好”、“差”),我希望系统创建一个新的代码行,将这些字符串作为输入

如果admin输入:'good', 'bad', 'average', 'bad', 'good', 'poor',我希望.py243r行之后有一个新行,如下所示:

rule243 = #
rule244 = scikit-fuzzy.control.Rule(input1["good"] & input2["bad"] & input3["average"] & input4["bad"] & input5["good"], output["poor"]

有人能帮我吗


Tags: 文件代码py规则scikitrulecontrolfuzzy
1条回答
网友
1楼 · 发布于 2024-05-13 18:57:39

事实上,这应该是可行的,我有一个解决方案,让我们实现它

小心

  • 这需要很高的验证,因为这是python代码,所以您不希望授予管理员从管理页面执行任何python代码的所有权限。如果管理员详细信息被劫持,你就完了$#埃德

让我们把问题分析成更小的问题

大点

  • 我们想从管理页面执行代码

较小的点

  • 我们需要设置多个规则,它们是不可数的,这对于数据库模型来说是一个完美的工作

  • 我们将代码视为Rule对象。我们将把dictionary键看作RuleKey对象。我们会把inputoutput看作RuleType

  • 在您的例子中,input1['good']与RuleTypeX[RuleKey]相同,其中x是从1开始的整数

  • 我们将RuleKey视为与Rule的多对多关系,规则可以有许多RuleKey对象,这些RuleKey对象可以存在于不同的Rule对象中

需要考虑的事情

  • 验证非常重要,您不希望有人在代码中传递eval(),并执行他想要的任何操作。

我创建了这个REGEXP来匹配您的代码

scikit-fuzzy\.control\.Rule\(\s?((input[0-9]+\[("|')(poor|good|average|bad|whatever)("|')\])\s?&?\s?)+,?\s?(output[0-9]*\[("|')(poor|good|average|bad|whatever)("|')\])+\s?\)

花了很多时间lol,在Regexr上测试,这很好地匹配了您的代码,将您的代码粘贴到那里并进行测试以查看,可以随意改进它,这不是这里的主要重点

class RuleKey(models.Model):
    class TYPES(models.IntegerChoices):
        INPUT = 0
        OUTPUT = 1

    content = models.CharField(max_length=10)
    type = models.IntegerField(choices=TYPES.choices)  # choices are of type Integer, the field must also be Integer

    def __str__(self):
        return self.content

content将包含['poor', 'good', 'whatever']中的任何一个,类型将为0表示Input,1表示Output我的意思是将其大写,因为我们也需要解决caps问题

我们有一个函数,在对最终代码求值之前检查它是否有效

def valid_python_code(code):
    if re.match(valid_code_pattern, code):
        return True

    return False

不要使用django提供的愚蠢的RegexValidator,如果不匹配,它会引发异常,但是如果匹配,它将返回NONE,对我来说没有意义,或者使用它,不管怎样

When we run code, We're 'executing' it and this is the formal word for running code.

class Rule(models.Model):
    keys = models.ManyToManyField('RuleKey', related_name='rules')
    wasExecutedBefore = models.BooleanField(default=False)  # ran before or no

    def execute(self):
        if self.wasExecutedBefore:
            return None

        input_keys = self.keys.filter(type=0)
        output_keys = self.keys.filter(type=1)

        """
        Can't use f-strings as we're evaluating genexps here, note the key.type, In a production environment
        This should be RuleKey.TYPES.INPUT or RuleKey.TYPES.OUTPUT, just swap the places of the classes, I'm lazy to do that
        """
        code = "scikit-fuzzy.control.Rule({inputs}, {outputs})".format(
            inputs=''.join(
                f"{key.get_type_display().lower()}{i + 1}['{key.content}']" for i, key in enumerate(input_keys) if
                key.type == 0),
            outputs=''.join(
                f"{key.get_type_display().lower()}{j + 1}['{key.content}']" for j, key in enumerate(output_keys) if
                key.type == 1))

        if valid_python_code(code):
            return eval(code)
        else:
            raise ValueError('Invalid code or missing libs or whatever.')

请允许我在这里评论一下我在实现此功能时解决的问题

  • RuleKey.TYPES是一个IntegerChoices类,Django接受属性并大写第一个字母并返回它,您的代码需要input而不是Input,幸运的是我们有str.lower()
  • 第二个问题是,当循环range对象以外的对象时,常规for in循环没有计数器,我们将使用enumerate
  • 第三个问题是Python-3.6中引入的f字符串(不确定是否为3.6)不支持GENEXPS在内部,您必须使用.format()
  • 第四个问题是,当直接在格式内部使用GENEXP而不是使用''.join(GENEXP)时,您将打印它的延迟版本,即<generator object at 0x214312>
  • 不要试图在一个查询中使用keys = keys.objects.all(),您将面临的问题将与foor循环计数器有关

  • 不要使用RuleKey.type,这将返回整数,您需要调用django的get_foo_display,在本例中,它是get_type_display()

让我们举例说明

我将按照以下格式命名变量,class\u type\u content

keys = [key_1_poor, key_0_good, key_1_average]

让我们循环并过滤

for key in keys: # no count  
    pass

让我们列举一下

for i, item in enumerate(keys):
    print(i) # prints index
    print(item) # prints object

现在对于.all()部分,如果您尝试迭代.all(),您会成功,但是,如果您的输入以input1开始,您的输出将以output2开始,这是因为两者都在同一个Queryset对象中

现在,将它添加到admin页面中,可能作为一个按钮

# admin.py

class RuleAdmin(admin.ModelAdmin):
    def response_change(self, request, rule_obj):
        if "execute" in request.POST:
            try:
                obj.execute() # execute (DONT IGNORE THE NOTES BELOW)
                obj.wasExecutedBefore = True 
                obj.save()
            except (ValueError, TypeError): # eval failed or execute
                pass

            return redirect(".")
        return super().response_change(request, obj)


admin.site.register(Rule, RuleAdmin) # register with admin

注意您需要导入re和所有必需的库,如scikit,以及models.py中代码所需的库

现在在您的应用程序中,我将我的应用程序命名为core,创建templates/admin/core/rule,格式如下<app_name/templates/admin/app_name/model_name>所有字母都小写

添加名为change_form.html的文件

加上这个

{% extends 'admin/change_form.html' %}

{% block submit_buttons_bottom %}
    {{ block.super }}
    <div class="submit-row">
            <input type="submit" value="Execute the rule" name="execute">
    </div>
{% endblock %}

这创造了发布execute的表单,然后我们在模型管理中捕获它,如果它存在,我们执行代码

我根据这个主题写了this dev.to article

相关问题 更多 >