7

(编辑:我得到了很多关于实现的答案(我很感激),但我更关心的是规范语法。这个插件的用户(即 Python/Django 开发人员 - 不是网站用户)需要指定条件, 在我发明的语法中。所以重新表述这个问题......在编写模型或表单类时......原型 Python/Django 开发人员更喜欢以下哪种语法来指定表单字段的条件显示逻辑? )

我正在寻找一些关于最pythonic(可读,直接等)方式的建议,以指定比较运算符以供以后在比较中使用(通过javascript执行)。类似的东西(这只是我想到的一个例子——还有很多其他可能的格式):

comparisons = (('a', '>', 'b'), ('b', '==', 'c'))

稍后将在 Javascript 中对其进行评估。

上下文是我正在开发一个 Django 应用程序(最终作为插件分发),这将要求用户以我选择的任何语法编写比较(因此有关使其成为 pythonic 的问题)。比较将引用表单字段,并最终转换为 javascript 条件表单显示。我想一个例子是有序的:

class MyModel(models.Model):
    yes_or_no = models.SomeField...choices are yes or no...
    why = models.SomeField...text, but only relevant if yes_or_no == yes...
    elaborate_even_more = models.SomeField...more text, just here so we can have multiple conditions

    #here i am inventing some syntax...open to suggestions!!
    why.show_if = ('yes_or_no','==','yes')
    elaborate_even_more.show_if = (('yes_or_no','==','yes'),('why','is not','None'))

    #(EDIT - help me choose a syntax that is Pythonic and...Djangonic...and that makes your fingers happy to type!)
    #another alternative...
    conditions = {'why': ('yes_or_no','==','yes'), 
                  'elaborate_even_more': (('yes_or_no','==','yes'),('why','is not','None'))
                  }

    #or another alternative...
    """Showe the field whiche hath the name *why* only under that circumstance 
    in whiche the field whiche hath the name *yes_or_no* hath the value *yes*, 
    in strictest equality."""
    etc...

(挥手......使用model_form_factory()将MyModel转换为ModelForm......在字典中收集所有“field.show_if”条件并作为MyModelForm.conditions或其他东西附加到ModelForm......)

现在在模板中的一段 javascript 中,MyModelForm.condtions 中的每个条件都将成为一个函数,用于侦听字段值的变化,并显示或隐藏另一个字段作为响应。基本上(在伪Javascript/Jquery中):

when yes_or_no changes...
    if (yes_or_no.value == 'yes'){
        $('#div that contains *why* field).show(); }
    else {
        $('#div that contains *why* field).hide(); }

这里的目标是让最终用户在模型定义中以一种直接的、pythonic 的方式指定条件显示逻辑(可能有一个选项可以在表单类上指定条件,我认为这更像是“Djangonic”(? ?),但对于我的用例,他们需要进入模型)。然后我的幕后插件将其转换为模板中的 Javascript。因此,您无需编写任何 Javascript 即可获得条件表单显示。由于这将掌握在 python/django 开发人员手中,因此我正在寻找有关指定这些条件的最原生、最舒适的方法的建议。

4

5 回答 5

3

这是一个想法:

import operator as op

a, b, c = 10, 7, 7

def f1():
    print 10

def f2():
    print 20

comparisons = ((a, op.gt, b, f1), (b, op.eq, c, f2))

for lhs, oper, rhs, f in comparisons:
    if oper(lhs, rhs):
        f()

=> 10
=> 20

通过适当的表示,您可以动态指定比较运算符及其相应的操作 - 实现为函数。查看操作员模块以查看可用的操作员。

于 2013-08-28T16:19:59.177 回答
1

你想要的是一个简单的 JavaScript 表达式生成器。粗略的草图:

import json

class JSExpr(object):
    #base class
    def gen(self):
        """Generate the js for this expression."""
        raise NotImplementedError()

class CodeDump(JSExpr):
    def __init__(self, code_str):
        self.code_str = code_str
    def gen(self):
        return self.code_str

class PyLit(JSExpr):
    """Py literal to js literal, e.g. 4 --> 4, "hi" --> '"hi"' """
    def __init__(self, py_lit):
        self.py_lit = py_lit
    def gen(self):
        return "%s" % (json.dumps(self.py_lit),)

class If(JSExpr):
    """Generate an if statement from the given JSExprs."""
    def __init__(self, comp, if_true, if_false=None):
        self.comp = comp
        self.if_true = if_true
        self.if_false = if_false
    def gen(self):
        return "if (%s) { %s; } else { %s; }" % (
            self.comp.gen(), self.if_true.gen(),
            self.if_false.gen() if self.if_false else "")

class BinOp(JSExpr):
    """Helper for common binary operations ==, >=, etc."""
    op = None
    def __init__(self, left, right):
        if self.op is None:
            raise ValueError("Must sub-class and define '.op'")
        self.left = left
        self.right = right

    def gen(self):
        return "((%s) %s (%s))" % (self.left.gen(), self.op, self.right.gen())

class Eq(BinOp): 
    op = '=='

class StrictEq(BinOp):
    op = '==='

class Gt(BinOp):
    op = '>'

class And(BinOp):
    op = '&&'

class StrContains(JSExpr):
    """Non-bin op example"""
    def __init__(self, left, right):
        self.left = left
        self.right = right

    def gen(self,):
        return "((%s).indexOf(%s) != -1)" % (self.left.gen(), self.right.gen())

然后你所要做的就是以某种方式JSExpr从网页上的用户输入生成实例,并从中生成一个实例IfPyLit用于插入值:

>>> print If(StrictEq(PyLit(4), PyLit([1, 2, 3])),
       CodeDump("console.log('weird things happening');")).gen()
if (((4) === ([1, 2, 3]))) { console.log('weird things happening');; } else { ; }

>>> print If(Gt(PyLit(4), PyLit(2)),
         CodeDump("alert('you did it!');"),
         CodeDump("alert('nope');")).gen()
if (((4) > (2))) { alert('you did it!');; } else { alert('nope');; }

或者您可以使用CodeDump检查变量名称。您问题的示例是:

>>> print If(Eq(CodeDump("yes_or_no.value"), PyLit("yes")),
     CodeDump("$('#div that contains *why* field').show();"),
     CodeDump("$('#div that contains *why* field').hide();")).gen()
if (((yes_or_no.value) == ("yes"))) { $('#div that contains *why* field').show();; } else { $('#div that contains *why* field').hide();; }

输出并不漂亮,但它应该可以工作。你可以花一些时间让它变得更好。

于 2013-08-28T17:36:56.573 回答
1

我真的很喜欢 SQLAlchemy 如何使用运算符重载来处理这个问题,所以你可能想看看他们的实现以获得一些想法。例如,这是它如何进行查询...

session.query(User).filter(User.name == 'fred')

如您所见,User.name == 'fred'实际上创建了一个新对象,然后可以将其转换为 SQL 字符串。这不需要是字符串,您可以根据需要将其转换为 JSON 对象。当然,这取决于让 User.name 支持这种模型,我认为 Django 的模型不支持。

但是,你可以做类似的事情,像这样......

class Wrapper(object):
    def __init__(self, prop):
        self.prop = prop

    def __eq__(self, x):
        return "{0} == '{1}'".format(self.prop, x)

def test(x, fun):
    w = Wrapper(x)
    return fun(w)

class MyModel(object):
    yes_or_no = 'yes_or_no'

print test(MyModel.yes_or_no, lambda x: x == 'yes') # "yes_or_no == 'yes'"

“Wrapper”对象是支持实际操作符重载的对象。在此示例中,我们返回一个字符串,但您可以返回另一个 Wrapper 对象以允许复合语句(例如(a == 2) & (b == 4))。究竟如何“包装”对象取决于您。您还可以包装模型以使复合语句更容易...

model = f(MyModel)
print model.yes_or_no == 'yes' | model.yes_or_no == 'no'
于 2013-08-28T16:52:57.220 回答
1

您如何看待使用 django 的语法进行查询?

>>> Entry.objects.filter(
...     headline__startswith='What'
... ).exclude(
...     pub_date__gte=datetime.date.today()
... ).filter(
...     pub_date__gte=datetime(2005, 1, 30)
... )

所以它可能看起来像:

class MyModel(models.Model):
    yes_or_no = models.SomeField()
    why = models.SomeField(yes_or_no='yes')
    ... or ...
    why = models.SomeField(field__yes_or_no='yes')
    ... or even ...
    why = models.SomeField(field__yes_or_no__contains='ye')
于 2013-08-28T16:32:58.480 回答
0

这可能有点矫枉过正,但您可以使用 Pyjaco 工具将 Python 源代码编译为 Javascript 源代码。然后你可以用普通的 Python 编写你的比较。很难比原生 Python 更 Pythonic!

于 2013-08-28T17:51:51.707 回答