Самый простой способ - использовать функцию tuple _ , снабженную SQLAlchemy:
from sqlalchemy import tuple_
session.query(Foo).filter(tuple_(Foo.a, Foo.b, Foo.c).in_(items))
Это работает с PostgreSQL, но ломается с SQLite.
К счастью, существует обходное решение, которое должно работать во всех базах данных.
Начните с отображения всех элементов с помощью выражения and_
:
conditions = (and_(c1=x, c2=y, c3=z) for (x, y, z) in items)
И затем создайте фильтр or_
, который охватывает все условия:
q.filter(or_(*conditions))
Вот простой пример:
#/usr/bin/env python
from sqlalchemy import create_engine
from sqlalchemy import Column, Integer
from sqlalchemy.sql import and_, or_
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///')
session = sessionmaker(bind=engine)()
Base = declarative_base()
class Foo(Base):
__tablename__ = 'foo'
id = Column(Integer, primary_key=True)
a = Column(Integer)
b = Column(Integer)
c = Column(Integer)
def __init__(self, a, b, c):
self.a = a
self.b = b
self.c = c
def __repr__(self):
return '(%d %d %d)' % (self.a, self.b, self.c)
Base.metadata.create_all(engine)
session.add_all([Foo(1, 2, 3), Foo(3, 2, 1), Foo(3, 3, 3), Foo(1, 3, 4)])
session.commit()
items = ((1, 2, 3), (3, 3, 3))
conditions = (and_(Foo.a==x, Foo.b==y, Foo.c==z) for (x, y, z) in items)
q = session.query(Foo)
print q.all()
q = q.filter(or_(*conditions))
print q
print q.all()
Какие выходы:
$ python test.py
[(1 2 3), (3 2 1), (3 3 3), (1 3 4)]
SELECT foo.id AS foo_id, foo.a AS foo_a, foo.b AS foo_b, foo.c AS foo_c
FROM foo
WHERE foo.a = :a_1 AND foo.b = :b_1 AND foo.c = :c_1 OR foo.a = :a_2 AND foo.b = :b_2 AND foo.c = :c_2
[(1 2 3), (3 3 3)]
Это короткий отрывок из одного из моих тестовых файлов, где self.render_template является простым вспомогательным методом в TestCase:
rendered = self.render_template(
'{% load templatequery %}'
'{% displayquery django_templatequery.KeyValue all() with "list.html" %}'
)
self.assertEqual(rendered,"foo=0\nbar=50\nspam=100\negg=200\n")
self.assertRaises(
template.TemplateSyntaxError,
self.render_template,
'{% load templatequery %}'
'{% displayquery django_templatequery.KeyValue all() notwith "list.html" %}'
)
Он очень простой и использует тестирование методом черного ящика. Он просто берет строку в качестве источника шаблона, отображает ее и проверяет, соответствует ли результат ожидаемой строке.
Метод render_template
довольно упрощен:
from django.template import Context, Template
class MyTest(TestCase):
def render_template(self, string, context=None):
context = context or {}
context = Context(context)
return Template(string).render(context)
Вы, ребята, направили меня на верный путь. Можно проверить, что контекст был правильно изменен после рендеринга:
class TemplateTagsTestCase(unittest.TestCase):
def setUp(self):
self.obj = TestObject.objects.create(title='Obj a')
def testViewsForOjbect(self):
ViewTracker.add_view_for(self.obj)
t = Template('{% load my_tags %}{% views_for_object obj as views %}')
c = Context({"obj": self.obj})
t.render(c)
self.assertEqual(c['views'], 1)
Если вы не хотите менять структуру на что-то другое , у вас действительно нет выбора - вам понадобится большой оператор if, чтобы указать, с каким полем вы имеете дело. Вы можете скрыть это (и упростить запись) с помощью макросов, но это та же самая структура, и вам придется просто с ней разобраться.
Вот пример того, как вы могли бы написать этот макрос - он действительно упрощает использование, но он все еще не «короткий».
//Assumption: at the time you want to use this, you've got two strings, one with the
// name of the field to set (key), one with the value to set (value). I also assume
typedef struct MyType {
int fieldA;
int fieldB;
} MyType;
// fldnamedef - name of the field in the structure definition (const char *)
// convfunc - name of a function that takes a value, returns a fldtypedef
// s - structure to put data into
// key - const char * pointing to input field name
// value - const char * pointing to input field value
#define IF_FIELD_SET(fldnamedef, convfunc, s, key, value) {\
if (strcmp(#fldnamedef, key) == 0) {\
s.fldnamedef = convfunc(value);\
}\
}
int main()
{
MyType t={0,0};
IF_FIELD_SET(fieldA, atoi, t, "fieldA", "2");
printf("%d,%d\n",t.fieldA, t.fieldB);
}
А вот вывод препроцессора, в который преобразуется строка IF_FIELD_SET:
Когда я тестировал свои теги шаблона, я бы сам тег возвращал строку, содержащую текст или dict I работал с ... вроде как с другим предложением.
Поскольку теги могут изменять контекст и / или возвращать строку для визуализации, я обнаружил, что быстрее всего просто просмотреть визуализированную строку.
Вместо:
return ''
Получите:
return str(my_data_that_I_am_testing)
Пока вы не будете счастливы.