1 | class Model(object):
|
---|
2 | def __init__(self, table_name):
|
---|
3 | self.table_name = table_name
|
---|
4 |
|
---|
5 | def query(self, *result):
|
---|
6 | return Query(self.table_name, result)
|
---|
7 |
|
---|
8 | class Query(object):
|
---|
9 | def __init__(self, table, clauses=None):
|
---|
10 | if (clauses):
|
---|
11 | self.object = WhereAnd(clauses)
|
---|
12 | else:
|
---|
13 | self.object = None
|
---|
14 |
|
---|
15 | self.results = None
|
---|
16 | self.len = None
|
---|
17 | self.table = table
|
---|
18 | self.parent = None
|
---|
19 |
|
---|
20 | def __iadd__(self, other):
|
---|
21 | "Results from self += other"
|
---|
22 | assert self.table == other.table, "Table mismatch in queries."
|
---|
23 | assert self.parent == other.parent, "Parent mismatch in queries. They both aren't subqueries of the same query."
|
---|
24 | self.object = WhereOr((self.object, other.object))
|
---|
25 | self.results = None # Since we're modifying this, delete our caches
|
---|
26 | self.len = None
|
---|
27 | return self
|
---|
28 |
|
---|
29 | def __add__(self, other):
|
---|
30 | "Results from (self + other)"
|
---|
31 | assert self.table == other.table, "Table mismatch in queries."
|
---|
32 | assert self.parent == other.parent, "Parent mismatch in queries. They both aren't subqueries of the same query."
|
---|
33 | q = Query(self.table)
|
---|
34 | q.object = WhereOr((self.object, other.object))
|
---|
35 | q.parent = self.parent
|
---|
36 | return q
|
---|
37 |
|
---|
38 | def _get_final_object(self):
|
---|
39 | "Resovles the final object in case this Query has a parent (is a sub-query)."
|
---|
40 | if (self.parent == None):
|
---|
41 | return self.object
|
---|
42 | else:
|
---|
43 | return WhereAnd((self.object, self.parent.object))
|
---|
44 |
|
---|
45 | def fetch(self):
|
---|
46 | "Hits the database, and returns the results"
|
---|
47 | print "SELECT * FROM `%s` WHERE %r" % (self.table, self._get_final_object())
|
---|
48 | self.results = ["Bob"] # Since we don't actually have a database, return Bob.
|
---|
49 | return self.results
|
---|
50 |
|
---|
51 | def _count(self):
|
---|
52 | "If we don't actually use the results, and just need the length, this optimizes it a bit."
|
---|
53 | print "SELECT COUNT(*) FROM `%s` WHERE %r" % (self.table, self._get_final_object())
|
---|
54 | self.len = 1
|
---|
55 |
|
---|
56 | def sub_query(self, *clauses):
|
---|
57 | "Creates a sub-query, all results in that query come from the results of this."
|
---|
58 | q = Query(self.table, clauses)
|
---|
59 | q.parent = self
|
---|
60 | return q
|
---|
61 |
|
---|
62 | def __iter__(self):
|
---|
63 | "Container emulation"
|
---|
64 | if (self.results == None):
|
---|
65 | self.fetch()
|
---|
66 | return self.results.__iter__()
|
---|
67 |
|
---|
68 | def __len__(self):
|
---|
69 | "Container emulation"
|
---|
70 | if (self.results): # If we have results, return the length of those
|
---|
71 | return len(self.results)
|
---|
72 | elif (self.len == None): # If we don't have a cache of the length, query the database
|
---|
73 | self._count()
|
---|
74 | return self.len # We must have a cache of the length, so return that.
|
---|
75 |
|
---|
76 | def __getitem__(self, i):
|
---|
77 | "Container emulation"
|
---|
78 | if (self.results == None):
|
---|
79 | self.fetch()
|
---|
80 | return self.results[i]
|
---|
81 |
|
---|
82 | def __contains__(self, o):
|
---|
83 | "Container emulation | i.e. if (o in self):"
|
---|
84 | if (self.results == None):
|
---|
85 | self.fetch()
|
---|
86 | return o in self.results
|
---|
87 |
|
---|
88 | def __str__(self):
|
---|
89 | "Container emulation | print self"
|
---|
90 | if (self.results):
|
---|
91 | return str(self.results)
|
---|
92 | else:
|
---|
93 | return str(self.fetch())
|
---|
94 |
|
---|
95 | class WhereOr(object):
|
---|
96 | "OR clause"
|
---|
97 | def __init__(self, clauses):
|
---|
98 | self.clauses = clauses
|
---|
99 |
|
---|
100 | def __repr__(self):
|
---|
101 | return "(%s)" % " OR ".join([repr(c) for c in self.clauses])
|
---|
102 |
|
---|
103 | class WhereAnd(object):
|
---|
104 | "AND clause"
|
---|
105 | def __init__(self, clauses):
|
---|
106 | self.clauses = clauses
|
---|
107 |
|
---|
108 | def __repr__(self):
|
---|
109 | return "(%s)" % " AND ".join([repr(c) for c in self.clauses])
|
---|
110 |
|
---|
111 | class WhereClause(object):
|
---|
112 | "Operator clause | i.e. field < value"
|
---|
113 | def __init__(self, *args):
|
---|
114 | self.args = args
|
---|
115 |
|
---|
116 | def __repr__(self):
|
---|
117 | return "%s %s %r" % self.args
|
---|
118 |
|
---|
119 | class Field(object):
|
---|
120 | def __init__(self, name):
|
---|
121 | self.name = name
|
---|
122 |
|
---|
123 | def __lt__(self, other):
|
---|
124 | return WhereClause(self.name, "<", other)
|
---|
125 |
|
---|
126 | def __gt__(self, other):
|
---|
127 | return WhereClause(self.name, ">", other)
|
---|
128 |
|
---|
129 | def __eq__(self, other):
|
---|
130 | return WhereClause(self.name, "=", other)
|
---|
131 |
|
---|
132 | def __ne__(self, other):
|
---|
133 | return WhereClause(self.name, "<>", other)
|
---|
134 |
|
---|
135 | def __le__(self, other):
|
---|
136 | return WhereClause(self.name, "<=", other)
|
---|
137 |
|
---|
138 | def __ge__(self, other):
|
---|
139 | return WhereClause(self.name, ">=", other)
|
---|
140 |
|
---|
141 | def contains(self, other):
|
---|
142 | return WhereClause(self.name, "LIKE", "%%%s%%" % other)
|
---|
143 |
|
---|
144 | def startswith(self, other):
|
---|
145 | return WhereClause(self.name, "LIKE", "%s%%" % other)
|
---|
146 |
|
---|
147 | def endswith(self, other):
|
---|
148 | return WhereClause(self.name, "LIKE", "%%%s" % other)
|
---|
149 |
|
---|
150 | class FieldFactory(object):
|
---|
151 | def __getattr__(self, k):
|
---|
152 | return Field(k)
|
---|
153 |
|
---|
154 | def __getitem__(self, i):
|
---|
155 | return Field(i)
|
---|
156 |
|
---|
157 | # These would be imported, as in:
|
---|
158 | # django.models.app import polls, choices
|
---|
159 | # django.database import field
|
---|
160 | field = FieldFactory()
|
---|
161 | polls = Model('polls')
|
---|
162 | choices = Model('choices')
|
---|
163 |
|
---|
164 |
|
---|
165 | # The results will always be ['Bob'], because we aren't actually talking to a database.
|
---|
166 |
|
---|
167 | # Basic union:
|
---|
168 | results = choices.query(field.choice == 'A choice')
|
---|
169 | results += choices.query(field.votes > 4)
|
---|
170 | print results[0] # Database hit
|
---|
171 |
|
---|
172 | # Sub-query:
|
---|
173 | results = choices.query(field.choice == 'A choice')
|
---|
174 | popular_choices = results.sub_query(field.votes > 10)
|
---|
175 | print popular_choices # Database hit
|
---|
176 |
|
---|
177 | # Equivelant to the Sub-query
|
---|
178 | popular_choices = choices.query(field.choice == 'A choice', field.votes > 10)
|
---|
179 | print popular_choices # Database hit
|
---|
180 |
|
---|
181 | # LIKE / Startswith
|
---|
182 | obstaining = choices.query(field.choice.startswith("I don't know"))
|
---|
183 | print obstaining
|
---|
184 |
|
---|
185 | # Never called
|
---|
186 | never_results = choices.query(field.choice == 'There is never results, cause I am never used.')
|
---|
187 |
|
---|
188 | # Fetched
|
---|
189 | results = choices.query(field.choice == 'A choice')
|
---|
190 | results.fetch() # Database hit
|
---|
191 | print results # Cache used
|
---|
192 |
|
---|
193 | # Only counted
|
---|
194 | results = choices.query(field.choice == 'A choice')
|
---|
195 | print len(results) # Database hit, but only with a count
|
---|
196 |
|
---|
197 | # Uncomment to create an Assertion error, because we're adding two different table results
|
---|
198 | #results = polls.query(field.question == 'Love to error out!')
|
---|
199 | #results += choices.query(field.choice == 'A choice and a question? Throw Assertion!')
|
---|
200 |
|
---|