-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodel.lisp
169 lines (131 loc) · 4.71 KB
/
model.lisp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
;;
;; This model stuff follows the Django design very closely
;;
(in-package :php)
(eval-when (:compile-toplevel :load-toplevel :execute)
(defun gen-slots (slots)
(mapcar (lambda (slot)
(let ((name (string (if (listp slot) (first slot) slot)))
(initform (if (listp slot) (second slot))))
(list (find-symbol name) :initarg (intern name :keyword)
:initform initform :reader
(find-symbol name))))
slots))
(defmacro defclass* (class-name bases slots &rest options)
(list* 'defclass class-name bases (gen-slots slots) options)))
(defclass* field ()
(allow-null blank choices db-column db-index default non-editable
help-text primary-key unique verbose-name)
(:documentation "Base field class"))
(defclass* boolean-field (field)
()
(:documentation "Boolean field"))
(defclass* char-field (field)
(max-length)
(:documentation "Char field"))
(defclass* email-field (char-field)
((max-length 75))
(:documentation "Email field"))
(defclass* file-field (char-field)
(upload-to (max-length 100))
(:documentation "File-upload field"))
(defclass* image-field (file-field)
(height-field width-field)
(:documentation "Image Field"))
(defclass* url-field (char-field)
(verify-exists)
(:documentation "URL field"))
(defclass* ip-address-field (field)
()
(:documentation "IP Address Field"))
(defclass* text-field (field)
()
(:documentation "Large text are field"))
(defclass* xml-field (field)
(schema-path)
(:documentation "XML field with validation"))
(defclass* time-field (field)
(auto-now auto-now-add)
(:documentation "Time field"))
(defclass* date-field (field)
(auto-now auto-now-add)
(:documentation "Date field"))
(defclass* date-time-field (date-field)
()
(:documentation "Date/Time field"))
(defclass* integer-field (field)
()
(:documentation "Integer field"))
(defclass* positive-integer-field (integer-field)
()
(:documentation "Positive Integer field"))
(defclass* small-integer-field (integer-field)
()
(:documentation "Small Integer field"))
(defclass* positive-small-integer-field (small-integer-field positive-integer-field)
()
(:documentation "Positive small integer field"))
(defclass* decimal-field (field)
(max-digits decimal-places)
(:documentation "Decimal Field"))
(defclass* float-field (field)
()
(:documentation "Float field"))
(defclass* null-boolean-field (field)
()
(:documentation "Null Boolean Field"))
(defclass* slug-field (field)
((db-index t)
(maxlength 50))
(:documentation "Slug field"))
(defclass* foreign-key ()
(other-model)
(:documentation "Foreign key to other model"))
;; TODO many-to-many, one-to-one etc. see Django docs
(defclass model ()
((name :initarg :name :reader model-name)
(fields :initarg :fields :reader model-fields))
(:documentation "Model class, defines a relation"))
(defgeneric model-field (model name)
(:documentation "Retrive a field from the model"))
(defmethod model-field ((model model) name)
(getf (model-fields model) name))
(defclass query-set ()
((result-set :initarg :result-set :initform () :accessor result-set)
(from :initarg :from :initform () :accessor from)
(distinct :initarg :distinct :initform nil :accessor distinct)
(where :initarg :where :initform nil :accessor where)
(group-by :initarg :group-by :initform nil :accessor group-by)
(having :initarg :having :initform nil :accessor having)
(order-by :initarg :order-by :initform nil :accessor order-by)
(limit :initarg :limit :initform nil :accessor limit))
(:documentation "Query Set"))
(defun make-query-set (&key result-set from distinct)
(make-instance 'query-set :result-set result-set :from from :distinct distinct))
(defgeneric select (model &rest args &key &allow-other-keys)
(:documentation "A generic method that constructs a query set for all the objects in the model"))
(defmethod select ((m model) &rest args &key &allow-other-keys)
(declare (ignore args))
(make-instance 'query-set :from m))
(defun format-expr (e)
"format an SQL expression"
e)
(defgeneric format-sql (stream qs)
(:documentation "print formatted SQL expressio into stream"))
(defmethod format-sql (stream (qs query-set))
(format stream
"select ~@[distinct~* ~]~{~a~^, ~} ~@[from ~{~a~^, ~} ~]~@[where ~a ~]~@[group by ~{~a~^, ~} ~]~@[having ~a ~]~@[order by ~{~a~^, ~} ~]~@[~{limit ~a~^ ~@[offset ~a~]~}~]"
(distinct qs)
(result-set qs)
(from qs)
(format-expr (where qs))
(group-by qs)
(format-expr (having qs))
(order-by qs)
(limit qs)))
#|
(foreach-row (select goods :id (aref $_POST "id"))
(:tr (:td (:a :href (#|TODO: how do we describe the URLS|#) name))
(:td (:span :class "description" description))
(:td (:span :class "qty" qty))))
|#