(编辑:我得到了很多关于实现的答案(我很感激),但我更关心的是规范语法。这个用户插件(即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开发人员掌握,我正在寻找建议以最原生,最舒适的方式来指定这些条件。
答案 0 :(得分: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
通过适当的表示,您可以动态指定比较运算符及其相应的操作 - 作为函数实现。查看operator模块以查看可用的运算符。
答案 1 :(得分: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')
答案 2 :(得分: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'
答案 3 :(得分: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
个实例,并使用If
生成PyLit
来插入值:< / p>
>>> 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();; }
输出不是很漂亮但它应该有效。你可以花一些时间让它变得更好。
答案 4 :(得分:0)
这可能有点过分,但您可以使用Pyjaco工具将Python源代码编译为Javascript源代码。然后你可以用普通的Python编写你的比较。很难比本机Python更像Pythonic!