From bf22b5fb03c39982c17f7feb264c67ea62bdb652 Mon Sep 17 00:00:00 2001 From: Charles Leifer Date: Sun, 25 Sep 2011 19:01:26 -0500 Subject: [PATCH] Cleaning up the documentation some more --- docs/_static/peewee-white.png | Bin 0 -> 16537 bytes docs/_themes/flask/layout.html | 25 + docs/_themes/flask/relations.html | 19 + docs/_themes/flask/static/flasky.css_t | 395 +++++++++++++ docs/_themes/flask/static/small_flask.css | 70 +++ docs/_themes/flask/theme.conf | 9 + docs/conf.py | 8 +- docs/index.rst | 58 +- docs/peewee/models.rst | 6 +- docs/peewee/overview.rst | 62 +++ docs/peewee/querying.rst | 651 +++++++++++++--------- 11 files changed, 977 insertions(+), 326 deletions(-) create mode 100644 docs/_static/peewee-white.png create mode 100644 docs/_themes/flask/layout.html create mode 100644 docs/_themes/flask/relations.html create mode 100644 docs/_themes/flask/static/flasky.css_t create mode 100644 docs/_themes/flask/static/small_flask.css create mode 100644 docs/_themes/flask/theme.conf create mode 100644 docs/peewee/overview.rst diff --git a/docs/_static/peewee-white.png b/docs/_static/peewee-white.png new file mode 100644 index 0000000000000000000000000000000000000000..4526982d05284bcae42009a07f497feaac1e1bb5 GIT binary patch literal 16537 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
EV6r zc~tHBV`OI(5_k^_>?^Ee!+{`~6EMn;yRJ7IaL8!k2M1C6r)R4en-s)5%yR1~e@$BL zwCXszQ<*-M@*qn#Dpp6%mWNNev=7m=;e zG~ISeRj5$-P31?Q*=k$n(W0T4r@7LU24feJ>cshSg0x*U{3Vtld+vxwn6fvb_j>*4 z0jJ(4&bGp%g)`;@ad!cSOt{R|(!lcS_`bA65VZuvlHPJh#T}sOH{5@I!1$;D+#T&Q zOiacV+3titj6QrS%OB6&U46>TG#q*4de*#DZ;&hi`F<|s{jpJF+?}Fow@{N6BwYY& zdI+#%q~v>W6a29Doy)lrRsp8s9;2#;c-guIr=Qpu@du`B3SIn>P*cYTsHMZL!bhCX zJlhkNes=C=!7fZG(Scp?yzA$~IJ5cqV(~48k22edb*vhDXps$Mb*+W9-(OG{vT8a{ zIAr;Jz;@zeqct}fAR_X}(kSr`TzUS{#z&Uq1uimkKv+KDx)Gmne0%SuoU>qP1h|iI z+C$?Tow z4YEJPUTtynyu{@vskP_cTbS3g@^pm%*lpLVz>bpQ=k<+}T8rsgsAc*4x6$j-vwd?t zp~{4q(#ZGXrBUET1YMFLC*-xfj|h6%_VvcgE)y5?;Kze1rSMynX8u4jEk?eEGy8$< zq|MIG4(tkTy!i&TNfm;>Kf<4r#R<5c@#^ZOTX_LUvNEEjj04S`W7C2sfRbg$eIJFZvlvt+@4C8vkQ_k1@G ze=2gCWI5;EeMQD-pO!j_3hlzBlGV4+tGl4$wt4RYbt`X+?E8BPKM~O+JT5nPi_r@P zN1PH~Tb4H?C#x5ivoks}@7@BLVwacGar7%e-{EK1+9^2y?&v zW0Dv4^SuoRsD}hh(SVH3qFG|&la*G7MC2Q^T#@#;1YKWeYVM;qIMhJv`m$Pq+mg|y z?|i|t0OY=m??;ASzbVwZva*u@n0E{J?8bmw*TV(q@KOh1yh=+t_U@CB0 zEiDqVOj_J{Q(}}n6XGBf&%jKV=Ft3-*mxyi-(%cymgOCPhv~t>Iy!Jz4gIoq`uuzJ zDms@QFvDTr8a+RmuuIWTjFhtlCX6Mb4YH*h-pXf&Ps0lg{;3J9maLoIXEDen14|yu zI!rEy$$dGGD{8kU`eXZ6*Vx$7#yei7uC82?8r!@&@ZY8Ej^a6iF*HOh;dGI^<$FAt za9GWEo<4Kly*?*v;61)pA;19plWC#Q`)?YHQk^wy!VM$aR&HKjfIai^Exbwz|reKOzV8pkyHxDM`t%>RDNFF^|VfN~RYeEa-|-bDFSxke<4u*O*uQ<~P$1&Lv+_+leNn(O{c#oDzcn*PAwVq#r7k%ML=9XTc! zr_iw2@+jBhj+XkL5pv6WbHpf%`hQa5Nhq@cYc!GGF>9k(GRXu%dKQSCP+S+W!l0?u zaqb1WQ1i9b6fw7e)@PpTR$-`H2qaV0(gIMIQy-aGR%R^VJK;_Nd}iV2Jx)xF=jh82 zMKr?T4pW_P0#MJm{ab$Dt=IJQmXtG}r##4QFZOmr z@suYhciQnfdurd{i}7Mebo_p&OD6^qFXdzT7>j^FwevI!V6d?lada}=+P=2pgjF>i zQMrqO8UIz~4zwL?>m{J-M_&u_u`#9P=vuSqpzr^e6rQXepcB!klu=C3VMB!LN%9iB zdGKSrD~Sh5C^hb|}4x|^PleX#RoXSP5mp}e#Gb*nI zl53ZXGZJ?mVSUA{N2g&-Rop*ZDsZTmFC6b)?<*cJ-f3ZlU6Xqr1`KwLv8Gt8UZS!q zvt``u?oV0PbxaMT6ly%9F$f0P#H*xlxP~M4J(QsPQM?(ZH?N?M@3u(o#aXkS)Gq}| z746=0lK3PxYRk)re`fCpnyYR}3WW>JsIpeBbJ}&{HEx5$!}Qk@nDY%P<-Zr9Z?v_E ztn2lp%+S_q{@5r=LXFJ)&C)lM`LTlmBIGB|ywk589DUiD2dT0Cnyo}d#P+j)Q?UvU zGeWgYGczQ_kTCVor%ca>RfyD~t*39CJq%2h#Hn>cvI>@0D-Hthqs*gsg`brmvGCO^r)KDsPg#KT-@w zk(4aByUuyA`}-F#&V_w$m!OK+bcvex6xrH3NU&}o1yIY$<)7_TGSO{Rn$qli#>CA0 z0>pa&X#$sIIU1wP(ZS2xR;GU8c|q#j%02LlEENx%x0z4T{_JmrW;sEN#-c&nIfYU4 zouMg*E;8uRN*-f%azkSil(~Q_R2un4__h7s+5$S5M|%E&HnHmH`CRRry$jMxrzoj> z3=9k|e&>`=j}x{P?_R2K@UesVTdbJf+*vsJkv3HE{rhl5B4dzW{j4hp#hyu#uGi}@ z%K(^u+P``9Md@4i-r>vW@pSPU-8wk}=^k!>x;4Y@e6-==w(jOSwxr!d|2?}OKqz5m zwuNYp=zNn5dca&O)JxRs<;~izwFGA?J-(`#I&~X}Sr(_U=N0U3ERQuYV6QLiV=X49&=Y^X2&Nprl}- zD03xz-1?6V_g+ax^fHmxM()i0r(iiY#=8%Br!J`caDr!GStus{EiF-#c*zR&Y4O3;Hn21VMM9P6q z$ONk`#dRu^Nm&a=kFy@FFC}uxdY_k9L1?Py9Oe#qGKxO9Svk9=WUdxD(u3EN!kWqJ6_9h5RRxkdwl4?7lAgGM7kH7C ziuD?=1HX2;8-mOw$OPN?*W)kjZ|i*Ihg8*0+dK!BF?nO^H{mE~;S$AhtFT#?R(*Zo zDepbQrI?~tPpCt4br@?~n#7<+#l?*YZ5=@q)waO;-mulRwJ6Zxez5gOkt*Z*`A=wW z+KB7@o>n|2CUNH1jlt_Pq~MO*VY{5m<$z3$n%_OC#vis8bbq!nWFk$JOgFpV3$#C6 zkIXJDeDgH<6nNoMgK(JN|%t#uKe!CN_72-5m+Hn!TS23L6g|g z3PRCz47_EdPIu{gsLC5oN>iiG=>A6Nw4;&P2$A*r$0xULAb{bMKGY zAyEH-^Y!-pK(91yklunpg4-cpU41=?cUp)Y)RPK@b?9;HpM?e1ZYIlFhDHEi+owqs zCsR=8qFCCUMaHXRq?4;&o~Yt!mW7eop#P?Uhqu}LUI3`lOQnJ6#+>hmUMagLoDj~; zJj2k+@5k981)`sh`NPzkc+9`Mdx!MND(fdA*B^-yu`5F+KduX~b|$|t1|4e2k4SI3VILv(HYsLp~9(i1oOuEAzkq%Imj4aK;+?%OXur;&3n zaT9rehL5e^{9b9gftk!adPyh=weUH7VbKpnX!@d zFEp@RJv7Ap`xp4wV0OD0H|67a0DumFHMaHGmAAV@h{|2u%9>g|W*O%fms}je$^~O9 zYXfXa!{%)b$jE@01c(yHt^A5G0WV6%83hHqGN#N1|IC|bdWw-KW+y{Nh|+3nU-u>= zoE;+sSkP22ilXb;pYpd7iI5~ND}e-Gq5Xq>U;*@-8c|}@zpu`@y9x@@jMyd>Hn}$p zV0l%6pH6oFLF_=Ngli0LH~XFu7WxHQv-GCBZb!YwEh&qJ^KI1XZhc%XQm^8A7)Pcr zDn3RfRpLIRMGzEC1ajc@SJY~4355>4fTS!=NGA)8^yOB0}$f?e@MDOvYmmWX)R7_oSp`V{$oKQWU z1=63N4uFZf&N0S3xsCJfO|}1VQxqU>1!%j z0DSH;GmBMG0xUN<1vz`*Ef2ubA{$OV&dw{%Vm-UJnfBkmSJzg5n*7McDbT-veO8<| z(fr;kVt@5nkjdxWy1H-UHr&dBrMNE^ptGgyx2O?m0)mR{_yWR5syBDh*Fu%pQf3^% z9n{ffrn-caU^K5T2riJ*8WWBo@}GeWlt#OZ6EUc0i{tP$#9;^P)%TAt2np)z^ED`c zZ_NaoC-+T*uVLu#7QNzG*3vIcrk?ZWr>AOa73wfLT~56@Z6+6K&pFw(K?xU3(j$lQ z1Nky#pdCBieDCV&kW?fpCvBY_&9VxwpV-R1L6RF36!ybR>={29fQ*!sx&-^4MAc|RmhTOzV&5{RuGuz+DPX@lBBU8 zFb(uf4HNV%owhai@lEIVjF;n%_Te;h^KQedEP{2Nr|aIRTKc2Ys6?0~)amBe5L+3y zh;bA8=GvD3JK*PcKne^1VW8pI3uE}Js46=?Eis16!}x2<;VfU1ra$ZgT;c@Y%c@U2 zJN2iLBP(mSf7SRp#S0Z`)J$A0!d9c44;?;Mw=_wW&(fvHvGa3D7Bvz{Yw-*4nH)~N z>F>b>18-5gHa9o7V=qHY^L}+^36k$0=GzHK0$Wk`2$v6oyL=tW#;oxuoEZEnDoZzq z#y?-nw=i#Za!4M3z|OT2SYsYMd$uiK)T;J2P6j$Wbt7OmpvYL(IkSOemXuUX^S)|Y zS6_G4#4**0Zm>Cq2xQk-T8^u7q3_!t{oPcpvc_Yo+I$3PWWk1kUWy#>%JSCC>1D5w zgCS)e?e~D;f)OAZ^Zo7~r6$3YCUu>g= z2V-S5?p|=Zd!#(%i6aEB9>256f0{~dxYB0Hy=M%Ce-Rd1tKbdn(06Q;uhX{ja{HI} z;anCYHWpBlch5v?q!7E9zsaOWq_%eYL!cIf3`jnzs|Q2AFrdY zm#_&#*c1eWiDaf2LIdt!I>0g*3?(N zX_C_zEiLb)%y77&6Qf2Intc?T)efmroZ{SsG86572SdZdCvOybI3ePExV`W*k!m!@ z*2y9R)&d)WRVT>e=SC+WKsNn#>CNl@P4pw^y-19!B50>uj*PfGtg$3|g<972EzWy3 z|Lu^a)*Q~x-0U3Kj(nYOED&ll&!e9?Z{U4|9)EB;zL}bqM%lG}w7~m=B_{4dS)}C@ zCr>SHpvo}6mEjFhWE`TBlf{0jnWL z6c?>y%c8+P4_w7Ave4bA<=1{c#YmvQo%zNufUV1{%S#C5prPmRFY61}m?o9&r8UpM zV+!2TCUVkbI(Z+3A31j$45EpuUEA+831H4M%YxK7BkOvYlnL@>%<_!564P(v>tN)R zlp0l&W11nAZeP^++~!V?bu|mmW}N?~L9>LT!*GjWik|dwqAQZ>zZ%|PVkOKh&K7Di z;;44ZfBQmADcQ#UAH&)nuAw`9a{UvKL>`_VHaz-iMG0QCDx8aG5}y{cg!_9qqw{g& zq~$$}kJECN>x#>mMB0hE%$%wNf30@lG*~1Zs^F>if&%F3K7d=<0&I)HA+hr04%PlE z;xA;&PqkI@$d%(Qp4#`RS)t$R-zP;bM?j%A50||3F5OfFqu086vvk5UlGOOQ4qhjr z5|Lfx*Am%bo651~pi!ZMcWXrCIl?=XKG& zi@$I6dmP?CTvG=Vowq$T~Vlu#LNTo`vN-=L56 z5B!#;!&&#shd8)vasvJ=6 zh(09UJveq=4_sdIUfzH~MO1w0U6zGTe`PqL{d|YJxS!tmv#eyJxrTdUj!86$0p!r} z*;`cz(j~!8Gi?k6?Ws#i2&?K~ zNrjl^JSlQtDYnnN1+BX?R%zt({(%fHhH1yj^P!@^vqjWQ<>xKpHjbG&kLRiTn|k(> zEV#CxRKq0*3g20@e1x7oN+HomM2h}0D0;jfX?q;KehlC`*cJ|OJ)nHt4!n*UZO>JkrcuVc{$8Mmi&$4Qp2|W<&?g zXEDam^1UV@gG7(uQHJHh|4fAniq1kfU~s+iD&X?rZP}zil+@Q3M8)cF<815u>zS#} z<{Os^)fn+GWUIq`#!C)YnqISPV@ozVol{4%I~$-5h=@&+5_^fG6__9}jd-kT>0SfJ z5LRR}=t_4G+&`dX7j6Ho&J*FFFJ<;2Jp6`38EdwHyz%3rg{NnNWnGl$MnU23Or_Xp zuS;}Oe(>gx>N(7minSI8mR6KK}nA^A;=X^;;tk7}DklNAheRq)J1`ARlYh~N6{XQOj? z(#EZRTYLL!nwgSmUE_IkD_^Q|2RsZEViFQllWHt%`;YcDEiK}$lk~W8>^wY5+&HqM zHZo39B%vXd-fGFz(Wb$pYWU9$0o<5nzR1w`g_67E%Yf5PEKjA zt3Us3)_Uc_XP0Wn!n%Y?nfF&i6jK(ZnAW5qLGGHQ*7-!Syt*=ePfWg>b_|%AeX8%x z%6u&_16|S~$0c>j zp}|{U<1XrC1s5Ne;D-m$YBQ(O0uE5UEFK#RlcI2IQKq&7A5n>te{`-dA zZN`+7mNp;T?^0;Q%I@r>rNvuKCR9Ar@bKDy6}ziVrGmm6VdA@j5>^nr2+i0pEiY{^3g>`5=UXlBD~rg@u)+ zuB8PWaL8jz-uSbNJKF&cCFa()#*BT(YrwGQ3bpCw@DBJ}92rTjDlE!D4?zUyM(p63 zIz{q%?7hvqTn5cPQJTil&_&^`EyB`iQgcq5qG1)6)lPogJ}}J?GE}epT?Ms^o1=ob zD3SV?$jqLcloWojtRdgN8tKSHs=}9%1P&2(6O-H=wcmMg@0oaIn|!fS&6U$+lGAH) zFf3Q0U8+@PLM(KFA(IslOO4Xo=u2?Za_dfUDe{%*;vb5l)FK_KiyKThp5PvA`l?(c zY=znHE!x)HMY&hDY=%$)dcP-H8V46{ z2+hQ)h?gI}{2UH2WV^lRTQrXV-@SE7^{8?I9XewsKUjx9X#1mgyoUsT`q&s_lPR^p zo;MnJdG;dsliKeOZf?X~>}?OX(QujH1id>&H#6Q>5T~{Y{-1{dhA=2r@$@a7FSM=E zKLFSYx% z+stpdO;}A4VVAnR{l^PufcxzpE$w(K&Caa|oQ&*lv-joU2;1m|zf44ge6a>!{LpX` z!@}MC_S3iNLFGTXAq{Z1*ZBU7jFe0Gi#A2}a*L2C-PmAb_mCCkGZ~4c`3_Rs5m#f= zZ_$s-mKGP!z(mCAK0>-1II3sYEiLuU^GQID%fE~xBX#+w=Ed?6!Q8t-p^E_omm3s^fUc@r-M0AtFydd z;_Mv;j+&4~sqyj4h@`&4#cV(Jg88?9=T%=;KMc|IKWY;(q?()_h^5}ivd9miUOD;8 z6L>d~bG<+H2Fw6IPY($An?!Y10G~z+{-o-7m#pfz@U5kN$?)0wjadT@wOawYzfrYt@f7N>TP*hp9=gWJsdsEW3e*9RoEm1wO>(pqlRgm!b ztMinz;lQbkd)P7Xo>?J`7HfL?dM8Qs>T(|(1#&tZBndbhFlNslG1Jw<)|ZmXo_@EC zWOpHO_96y#`4%y`ar10E0S9L7fRdQo3NI0{0bAw=0bfIF_de zj^&vpsWd9`k#Cu^!e)MhW-^dZSJz*xV7A-cF!~yt1bDg17cZkLCFR2l%+kcMs&juq z$ajRNS2%IV=*B1Vekk4D%z;RwE4*U?LsJ*{eQdQ4UTANl+wNG z>M}@AUmYxc_ie%-aQ8e74JDIs3``7m*Av?5N+X*2^#yZPK(5#FlI@U~S=R z72(9F)X<(|{ilZ=1E}}1-=g33%zWIYXhekr9Qd=2;>N+*oy5j~fOe{8nbzDQ&xy`g z>+YV0{q4WV=l(e|b@FwywB#;M1?mU*G_487rSt^3;)$ z&ylaWZNc+p$eTUG7eA)P!7}}vFOBc+2LfwrpkZe7-`?4pfQj_2 zZO{*Zb9r1c^Q~fY@PSTvn%5XeoTsd{o?^3>`!&s z!?FHLf1!~sEI*|Qx-FT1;s{8n-Yw?<^gUw`pQw#&;lQmys_}|V-@UwAO{QWGsLg%- z^h~b^6Cu*|wLjhqPNDn1s8fgAWj~t#`x$Q>*ZN6rie^Hzk+YIXJ8;alGY|Vjhg?7I z2)y7A5y!zvd?dOckBp3zP?G|Yg7GQj;N#$^L!u)i8Ih!)z?znp79G41CoK2>?QI(l cw^*+B4mn>r_6EU!5`Z8tts+$^VG{g*026_b+yDRo literal 0 HcmV?d00001 diff --git a/docs/_themes/flask/layout.html b/docs/_themes/flask/layout.html new file mode 100644 index 000000000..5caa4e297 --- /dev/null +++ b/docs/_themes/flask/layout.html @@ -0,0 +1,25 @@ +{%- extends "basic/layout.html" %} +{%- block extrahead %} + {{ super() }} + {% if theme_touch_icon %} + + {% endif %} + +{% endblock %} +{%- block relbar2 %}{% endblock %} +{% block header %} + {{ super() }} + {% if pagename == 'index' %} +
+ {% endif %} +{% endblock %} +{%- block footer %} + + {% if pagename == 'index' %} +
+ {% endif %} +{%- endblock %} diff --git a/docs/_themes/flask/relations.html b/docs/_themes/flask/relations.html new file mode 100644 index 000000000..3bbcde85b --- /dev/null +++ b/docs/_themes/flask/relations.html @@ -0,0 +1,19 @@ +

Related Topics

+ diff --git a/docs/_themes/flask/static/flasky.css_t b/docs/_themes/flask/static/flasky.css_t new file mode 100644 index 000000000..b5ca39bc1 --- /dev/null +++ b/docs/_themes/flask/static/flasky.css_t @@ -0,0 +1,395 @@ +/* + * flasky.css_t + * ~~~~~~~~~~~~ + * + * :copyright: Copyright 2010 by Armin Ronacher. + * :license: Flask Design License, see LICENSE for details. + */ + +{% set page_width = '940px' %} +{% set sidebar_width = '220px' %} + +@import url("basic.css"); + +/* -- page layout ----------------------------------------------------------- */ + +body { + font-family: 'Georgia', serif; + font-size: 17px; + background-color: white; + color: #000; + margin: 0; + padding: 0; +} + +div.document { + width: {{ page_width }}; + margin: 30px auto 0 auto; +} + +div.documentwrapper { + float: left; + width: 100%; +} + +div.bodywrapper { + margin: 0 0 0 {{ sidebar_width }}; +} + +div.sphinxsidebar { + width: {{ sidebar_width }}; +} + +hr { + border: 1px solid #B1B4B6; +} + +div.body { + background-color: #ffffff; + color: #3E4349; + padding: 0 30px 0 30px; +} + +img.floatingflask { + padding: 0 0 10px 10px; + float: right; +} + +div.footer { + width: {{ page_width }}; + margin: 20px auto 30px auto; + font-size: 14px; + color: #888; + text-align: right; +} + +div.footer a { + color: #888; +} + +div.related { + display: none; +} + +div.sphinxsidebar a { + color: #444; + text-decoration: none; + border-bottom: 1px dotted #999; +} + +div.sphinxsidebar a:hover { + border-bottom: 1px solid #999; +} + +div.sphinxsidebar { + font-size: 14px; + line-height: 1.5; +} + +div.sphinxsidebarwrapper { + padding: 18px 10px; +} + +div.sphinxsidebarwrapper p.logo { + padding: 0 0 20px 0; + margin: 0; + text-align: center; +} + +div.sphinxsidebar h3, +div.sphinxsidebar h4 { + font-family: 'Garamond', 'Georgia', serif; + color: #444; + font-size: 24px; + font-weight: normal; + margin: 0 0 5px 0; + padding: 0; +} + +div.sphinxsidebar h4 { + font-size: 20px; +} + +div.sphinxsidebar h3 a { + color: #444; +} + +div.sphinxsidebar p.logo a, +div.sphinxsidebar h3 a, +div.sphinxsidebar p.logo a:hover, +div.sphinxsidebar h3 a:hover { + border: none; +} + +div.sphinxsidebar p { + color: #555; + margin: 10px 0; +} + +div.sphinxsidebar ul { + margin: 10px 0; + padding: 0; + color: #000; +} + +div.sphinxsidebar input { + border: 1px solid #ccc; + font-family: 'Georgia', serif; + font-size: 1em; +} + +/* -- body styles ----------------------------------------------------------- */ + +a { + color: #004B6B; + text-decoration: underline; +} + +a:hover { + color: #6D4100; + text-decoration: underline; +} + +div.body h1, +div.body h2, +div.body h3, +div.body h4, +div.body h5, +div.body h6 { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + margin: 30px 0px 10px 0px; + padding: 0; +} + +{% if theme_index_logo %} +div.indexwrapper h1 { + text-indent: -999999px; + background: url({{ theme_index_logo }}) no-repeat center center; + height: {{ theme_index_logo_height }}; +} +{% endif %} + +div.body h1 { margin-top: 0; padding-top: 0; font-size: 240%; } +div.body h2 { font-size: 180%; } +div.body h3 { font-size: 150%; } +div.body h4 { font-size: 130%; } +div.body h5 { font-size: 100%; } +div.body h6 { font-size: 100%; } + +a.headerlink { + color: #ddd; + padding: 0 4px; + text-decoration: none; +} + +a.headerlink:hover { + color: #444; + background: #eaeaea; +} + +div.body p, div.body dd, div.body li { + line-height: 1.4em; +} + +div.admonition { + background: #fafafa; + margin: 20px -30px; + padding: 10px 30px; + border-top: 1px solid #ccc; + border-bottom: 1px solid #ccc; +} + +div.admonition tt.xref, div.admonition a tt { + border-bottom: 1px solid #fafafa; +} + +dd div.admonition { + margin-left: -60px; + padding-left: 60px; +} + +div.admonition p.admonition-title { + font-family: 'Garamond', 'Georgia', serif; + font-weight: normal; + font-size: 24px; + margin: 0 0 10px 0; + padding: 0; + line-height: 1; +} + +div.admonition p.last { + margin-bottom: 0; +} + +div.highlight { + background-color: white; +} + +dt:target, .highlight { + background: #FAF3E8; +} + +div.note { + background-color: #eee; + border: 1px solid #ccc; +} + +div.seealso { + background-color: #ffc; + border: 1px solid #ff6; +} + +div.topic { + background-color: #eee; +} + +p.admonition-title { + display: inline; +} + +p.admonition-title:after { + content: ":"; +} + +pre, tt { + font-family: 'Consolas', 'Menlo', 'Deja Vu Sans Mono', 'Bitstream Vera Sans Mono', monospace; + font-size: 0.9em; +} + +img.screenshot { +} + +tt.descname, tt.descclassname { + font-size: 0.95em; +} + +tt.descname { + padding-right: 0.08em; +} + +img.screenshot { + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils { + border: 1px solid #888; + -moz-box-shadow: 2px 2px 4px #eee; + -webkit-box-shadow: 2px 2px 4px #eee; + box-shadow: 2px 2px 4px #eee; +} + +table.docutils td, table.docutils th { + border: 1px solid #888; + padding: 0.25em 0.7em; +} + +table.field-list, table.footnote { + border: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + box-shadow: none; +} + +table.footnote { + margin: 15px 0; + width: 100%; + border: 1px solid #eee; + background: #fdfdfd; + font-size: 0.9em; +} + +table.footnote + table.footnote { + margin-top: -15px; + border-top: none; +} + +table.field-list th { + padding: 0 0.8em 0 0; +} + +table.field-list td { + padding: 0; +} + +table.footnote td.label { + width: 0px; + padding: 0.3em 0 0.3em 0.5em; +} + +table.footnote td { + padding: 0.3em 0.5em; +} + +dl { + margin: 0; + padding: 0; +} + +dl dd { + margin-left: 30px; +} + +blockquote { + margin: 0 0 0 30px; + padding: 0; +} + +ul, ol { + margin: 10px 0 10px 30px; + padding: 0; +} + +pre { + background: #eee; + padding: 7px 30px; + margin: 15px -30px; + line-height: 1.3em; +} + +dl pre, blockquote pre, li pre { + margin-left: -60px; + padding-left: 60px; +} + +dl dl pre { + margin-left: -90px; + padding-left: 90px; +} + +tt { + background-color: #ecf0f3; + color: #222; + /* padding: 1px 2px; */ +} + +tt.xref, a tt { + background-color: #FBFBFB; + border-bottom: 1px solid white; +} + +a.reference { + text-decoration: none; + border-bottom: 1px dotted #004B6B; +} + +a.reference:hover { + border-bottom: 1px solid #6D4100; +} + +a.footnote-reference { + text-decoration: none; + font-size: 0.7em; + vertical-align: top; + border-bottom: 1px dotted #004B6B; +} + +a.footnote-reference:hover { + border-bottom: 1px solid #6D4100; +} + +a:hover tt { + background: #EEE; +} diff --git a/docs/_themes/flask/static/small_flask.css b/docs/_themes/flask/static/small_flask.css new file mode 100644 index 000000000..1c6df309e --- /dev/null +++ b/docs/_themes/flask/static/small_flask.css @@ -0,0 +1,70 @@ +/* + * small_flask.css_t + * ~~~~~~~~~~~~~~~~~ + * + * :copyright: Copyright 2010 by Armin Ronacher. + * :license: Flask Design License, see LICENSE for details. + */ + +body { + margin: 0; + padding: 20px 30px; +} + +div.documentwrapper { + float: none; + background: white; +} + +div.sphinxsidebar { + display: block; + float: none; + width: 102.5%; + margin: 50px -30px -20px -30px; + padding: 10px 20px; + background: #333; + color: white; +} + +div.sphinxsidebar h3, div.sphinxsidebar h4, div.sphinxsidebar p, +div.sphinxsidebar h3 a { + color: white; +} + +div.sphinxsidebar a { + color: #aaa; +} + +div.sphinxsidebar p.logo { + display: none; +} + +div.document { + width: 100%; + margin: 0; +} + +div.related { + display: block; + margin: 0; + padding: 10px 0 20px 0; +} + +div.related ul, +div.related ul li { + margin: 0; + padding: 0; +} + +div.footer { + display: none; +} + +div.bodywrapper { + margin: 0; +} + +div.body { + min-height: 0; + padding: 0; +} diff --git a/docs/_themes/flask/theme.conf b/docs/_themes/flask/theme.conf new file mode 100644 index 000000000..18c720f80 --- /dev/null +++ b/docs/_themes/flask/theme.conf @@ -0,0 +1,9 @@ +[theme] +inherit = basic +stylesheet = flasky.css +pygments_style = flask_theme_support.FlaskyStyle + +[options] +index_logo = '' +index_logo_height = 120px +touch_icon = diff --git a/docs/conf.py b/docs/conf.py index 813fd4cf7..dac6a449f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -91,15 +91,17 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = 'ADCtheme' +html_theme = 'flask' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +html_theme_options = { + 'index_logo': 'peewee-white.png' +} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". diff --git a/docs/index.rst b/docs/index.rst index 5f431666d..401403fd6 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -3,8 +3,6 @@ You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. -.. image:: peewee-white.png - peewee ====== @@ -13,66 +11,14 @@ peewee * provides a lightweight querying interface over sql * uses sql concepts when querying, like joins and where clauses - -Examples:: - - # a simple query selecting a user - User.get(username='charles') - - # get the staff and super users - editors = User.select().where(Q(is_staff=True) | Q(is_superuser=True)) - - # get tweets by editors - Tweet.select().where(user__in=editors) - - # how many active users are there? - User.select().where(active=True).count() - - # paginate the user table and show me page 3 (users 41-60) - User.select().order_by(('username', 'asc')).paginate(3, 20) - - # order users by number of tweets - User.select().annotate(Tweet).order_by(('count', 'desc')) - - # another way of expressing the same - User.select({ - User: ['*'], - Tweet: [Count('id', 'count')] - }).group_by('id').join(Tweet).order_by(('count', 'desc')) - - -You can use django-style syntax to create select queries:: - - # how many active users are there? - User.filter(active=True).count() - - # get tweets by a specific user - Tweet.filter(user__username='charlie') - - # get tweets by editors - Tweet.filter(Q(user__is_staff=True) | Q(user__is_superuser=True)) - - - -Why? ----- - -peewee began when I was working on a small app in flask and found myself writing -lots of queries and wanting a very simple abstraction on top of the sql. I had -so much fun working on it that I kept adding features. My goal has always been, -though, to keep the implementation incredibly simple. I've made a couple dives -into django's orm but have never come away with a deep understanding of its -implementation. peewee is small enough that its my hope anyone with an interest -in orms will be able to understand the code without too much trouble. - - Contents: --------- .. toctree:: - :maxdepth: 3 + :maxdepth: 2 :glob: + peewee/overview peewee/installation peewee/uses peewee/example diff --git a/docs/peewee/models.rst b/docs/peewee/models.rst index b23734221..2c75815de 100644 --- a/docs/peewee/models.rst +++ b/docs/peewee/models.rst @@ -175,7 +175,7 @@ Model methods :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" :rtype: :py:class:`Model` instance or raises ``DoesNotExist`` exception - Get a single row from the database that matches the given query. raises a + Get a single row from the database that matches the given query. Raises a ``.DoesNotExist`` if no rows are returned: .. code-block:: python @@ -274,7 +274,7 @@ Model methods >>> database.connect() >>> SomeModel.create_table() # <-- creates the table for SomeModel - .. py:method:: drop_table([fail_silently=False]) + .. py:classmethod:: drop_table([fail_silently=False]) :param fail_silently: If set to ``True``, the query will check for the existence of the table before attempting to remove. @@ -285,6 +285,6 @@ Model methods Cascading deletes are not handled by this method, nor is the removal of any constraints. - .. py:method:: table_exists() + .. py:classmethod:: table_exists() :rtype: Boolean whether the table for this model exists in the database diff --git a/docs/peewee/overview.rst b/docs/peewee/overview.rst new file mode 100644 index 000000000..dc6897434 --- /dev/null +++ b/docs/peewee/overview.rst @@ -0,0 +1,62 @@ +.. _overview: + +Overview +======== + +peewee is a lightweight `ORM `_ written +in python. + +Examples: + +.. code-block:: python + + # a simple query selecting a user + User.get(username='charles') + + # get the staff and super users + editors = User.select().where(Q(is_staff=True) | Q(is_superuser=True)) + + # get tweets by editors + Tweet.select().where(user__in=editors) + + # how many active users are there? + User.select().where(active=True).count() + + # paginate the user table and show me page 3 (users 41-60) + User.select().order_by(('username', 'asc')).paginate(3, 20) + + # order users by number of tweets + User.select().annotate(Tweet).order_by(('count', 'desc')) + + # another way of expressing the same + User.select({ + User: ['*'], + Tweet: [Count('id', 'count')] + }).group_by('id').join(Tweet).order_by(('count', 'desc')) + + +You can use django-style syntax to create select queries: + +.. code-block:: python + + # how many active users are there? + User.filter(active=True).count() + + # get tweets by a specific user + Tweet.filter(user__username='charlie') + + # get tweets by editors + Tweet.filter(Q(user__is_staff=True) | Q(user__is_superuser=True)) + + + +Why? +---- + +peewee began when I was working on a small app in flask and found myself writing +lots of queries and wanting a very simple abstraction on top of the sql. I had +so much fun working on it that I kept adding features. My goal has always been, +though, to keep the implementation incredibly simple. I've made a couple dives +into django's orm but have never come away with a deep understanding of its +implementation. peewee is small enough that its my hope anyone with an interest +in orms will be able to understand the code without too much trouble. diff --git a/docs/peewee/querying.rst b/docs/peewee/querying.rst index 19f03a07d..41ddb0020 100644 --- a/docs/peewee/querying.rst +++ b/docs/peewee/querying.rst @@ -1,3 +1,5 @@ +.. _querying: + Querying API ============ @@ -10,7 +12,7 @@ The "pieces" of a peewee query are generally representative of clauses you might find in a SQL query. All pieces are chainable so rather complex queries are possible. -:: +.. code-block:: python >>> user_q = User.select() # <-- query is not executed >>> user_q @@ -19,7 +21,9 @@ possible. [u'admin', u'staff', u'editor'] -We can build up the query by adding some clauses to it:: +We can build up the query by adding some clauses to it: + +.. code-block:: python >>> user_q = user_q.where(username__in=['admin', 'editor']).order_by(('username', 'desc')) >>> [u.username for u in user_q] # <-- query is re-evaluated here @@ -29,10 +33,10 @@ We can build up the query by adding some clauses to it:: Django-style queries ^^^^^^^^^^^^^^^^^^^^ -If you are already familiar with the Django ORM, you can construct select queries +If you are already familiar with the Django ORM, you can construct :py:class:`SelectQuery` instances using the familiar "double-underscore" syntax. -:: +.. code-block:: python # get the active users active_users = User.filter(active=True) @@ -53,10 +57,10 @@ using the familiar "double-underscore" syntax. Where clause ------------ -All queries except ``InsertQuery`` support the ``where()`` method. If you are +All queries except :py:class:`InsertQuery` support the ``where()`` method. If you are familiar with Django's ORM, it is analagous to the ``filter()`` method. -:: +.. code-block:: python >>> User.select().where(is_staff=True).sql() ('SELECT * FROM user WHERE is_staff = ?', [1]) @@ -64,30 +68,39 @@ familiar with Django's ORM, it is analagous to the ``filter()`` method. .. note:: ``User.select()`` is equivalent to ``SelectQuery(User)``. -The ``where()`` method acts on the ``Model`` that is the current "context". +The ``where()`` method acts on the :py:class:`Model` that is the current "query context". This is either: * the model the query class was initialized with * the model most recently JOINed on -Here is an example using JOINs:: +Here is an example using JOINs: + +.. code-block:: python >>> User.select().where(is_staff=True).join(Blog).where(status=LIVE) This query grabs all staff users who have a blog that is "LIVE". This does the -opposite, grabs all the blogs that are live whose author is a staffer:: +opposite, grabs all the blogs that are live whose author is a staffer: + +.. code-block:: python >>> Blog.select().where(status=LIVE).join(User).where(is_staff=True) -.. note:: to ``join()`` from one model to another there must be a - ``ForeignKeyField`` linking the two. +.. note:: to :py:meth:`~SelectQuery.join` from one model to another there must be a + :py:class:`ForeignKeyField` linking the two. -Another way to write the above query would be:: +Another way to write the above query would be: + +.. code-block:: python - >>> Blog.select().where(status=LIVE, user__in=User.select().where(is_staff=True)) + >>> Blog.select().where( + ... status=LIVE, + ... user__in=User.select().where(is_staff=True) + ... ) The above bears a little bit of explanation. First off the SQL generated will -not perform any explicit JOINs - it will rather use a subquery in the WHERE +not perform any explicit ``JOIN`` - it will rather use a subquery in the ``WHERE`` clause: .. code-block:: sql @@ -100,6 +113,10 @@ clause: SELECT t1.id FROM user AS t1 WHERE t1.is_staff = ? ) ) + +And here it is using joins: + +.. code-block:: sql # using joins SELECT t1.* FROM blog AS t1 @@ -110,7 +127,10 @@ clause: t2.is_staff = ? -The other bit that's unique about the query is that it specifies "user__in". +Column lookups +^^^^^^^^^^^^^^ + +The other bit that's unique about the query is that it specifies ``"user__in"``. Users familiar with Django will recognize this syntax - lookups other than "=" are signified by a double-underscore followed by the lookup type. The following lookup types are available in peewee: @@ -143,7 +163,7 @@ lookup types are available in peewee: case-insensitive check for substring ``__in``: - x IN y, where y is either a list of values or a ``SelectQuery`` + x IN y, where y is either a list of values or a :py:class:`SelectQuery` Performing advanced queries @@ -152,7 +172,7 @@ Performing advanced queries As you may have noticed, all the examples up to now have shown queries that combine multiple clauses with "AND". Taking another page from Django's ORM, peewee allows the creation of arbitrarily complex queries using a special -notation called **Q objects**. +notation called :py:class:`Q` objects. .. code-block:: python @@ -161,13 +181,17 @@ notation called **Q objects**. SELECT * FROM user WHERE (is_staff = ? OR is_superuser = ?) -Q objects can be combined using the bitwise "or" and "and" operators. In order -to negate a Q object, use the bitwise "invert" operator:: +:py:class:`Q` objects can be combined using the bitwise "or" and "and" operators. In order +to negate a :py:class:`Q` object, use the bitwise "invert" operator: + +.. code-block:: python >>> staff_users = User.select().where(is_staff=True) >>> Blog.select().where(~Q(user__in=staff_users)) -This query generates the following SQL:: +This query generates the following SQL: + +.. code-block:: sql SELECT * FROM blog WHERE @@ -175,7 +199,9 @@ This query generates the following SQL:: SELECT t1.id FROM user AS t1 WHERE t1.is_staff = ? ) -Rather complex lookups are possible:: +Rather complex lookups are possible: + +.. code-block:: python >>> sq = User.select().where( ... (Q(is_staff=True) | Q(is_superuser=True)) & @@ -191,7 +217,7 @@ Rather complex lookups are possible:: This query selects all staff or super users who joined after 2009 or before 2005. -.. note:: if you need more power, check out ``RawQuery`` below. +.. note:: If you need more power, check out :py:class:`RawQuery` Aggregating records @@ -213,7 +239,7 @@ This is equivalent to the following: Entry: [Count('id')], }).group_by(Blog).join(Entry) -The resulting query will return Blog objects with all their normal attributes +The resulting query will return ``Blog`` objects with all their normal attributes plus an additional attribute 'count' which will contain the number of entries. By default it uses an inner join if the foreign key is not nullable, which means blogs without entries won't appear in the list. To remedy this, manually specify @@ -237,7 +263,9 @@ In order to execute a query, it is *always* necessary to call the ``execute()`` method. To get a better idea of how querying works let's look at some example queries -and their return values:: +and their return values: + +.. code-block:: python >>> dq = User.delete().where(active=False) # <-- returns a DeleteQuery >>> dq @@ -274,330 +302,425 @@ and their return values:: [1, 2, 3, 4, 7, 8] -.. note:: iterating over a SelectQuery will cause it to be evaluated, but iterating +.. note:: + Iterating over a :py:class:`SelectQuery` will cause it to be evaluated, but iterating over it multiple times will not result in the query being executed again. QueryResultWrapper ------------------ -As I hope the previous bit showed, Delete, Insert and Update queries are all -pretty straightforward. Select queries are a little bit tricky in that they -return a special object called a ``QueryResultWrapper``. The sole purpose of this +As I hope the previous bit showed, ``Delete``, ``Insert`` and ``Update`` queries are all +pretty straightforward. ``Select`` queries are a little bit tricky in that they +return a special object called a :py:class:`QueryResultWrapper`. The sole purpose of this class is to allow the results of a query to be iterated over efficiently. In general it should not need to be dealt with explicitly. The preferred method of iterating over a result set is to iterate directly over -the ``SelectQuery``, allowing it to manage the ``QueryResultWrapper`` internally. +the :py:class:`SelectQuery`, allowing it to manage the :py:class:`QueryResultWrapper` internally. SelectQuery ----------- -``SelectQuery`` is by far the most complex of the 4 query classes available in -peewee. It supports JOINing on other tables, aggregation via GROUP BY and HAVING -clauses, ordering via ORDER BY, and can be sliced to return only a subset of -results. All methods are chain-able. +.. py:class:: SelectQuery -.. py:method:: __init__(self, model, query=None) + By far the most complex of the 4 query classes available in + peewee. It supports ``JOIN`` operations on other tables, aggregation via ``GROUP BY`` and ``HAVING`` + clauses, ordering via ``ORDER BY``, and can be iterated and sliced to return only a subset of + results. - if no query is provided, it will default to '*'. this parameter can be - either a dictionary or a string:: - - >>> sq = SelectQuery(Blog, {Blog: ['id', 'title']}) - >>> sq = SelectQuery(Blog, { - ... Blog: ['*'], - ... Entry: [peewee.Count('id')] - ... }).group_by('id').join(Entry) - >>> print sq.sql()[0] # formatted - SELECT t1.*, COUNT(t2.id) AS count - FROM blog AS t1 - INNER JOIN entry AS t2 - ON t1.id = t2.blog_id - GROUP BY t1.id - - >>> sq = SelectQuery(Blog, 'id, title') - >>> print sq.sql()[0] - SELECT id, title FROM blog + .. py:method:: __init__(model, query=None) + + :param model: a :py:class:`Model` class to perform query on + :param query: either a dictionary, keyed by model with a list of columns, or a string of columns -.. py:method:: filter(self, *args, **kwargs) + If no query is provided, it will default to ``'*'``. this parameter can be + either a dictionary or a string: + + .. code-block:: python + + >>> sq = SelectQuery(Blog, {Blog: ['id', 'title']}) + >>> sq = SelectQuery(Blog, { + ... Blog: ['*'], + ... Entry: [peewee.Count('id')] + ... }).group_by('id').join(Entry) + >>> print sq.sql()[0] # formatted + SELECT t1.*, COUNT(t2.id) AS count + FROM blog AS t1 + INNER JOIN entry AS t2 + ON t1.id = t2.blog_id + GROUP BY t1.id + + >>> sq = SelectQuery(Blog, 'id, title') + >>> print sq.sql()[0] + SELECT id, title FROM blog - :param args: a list of ``Q`` or ``Node`` objects - :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + .. py:method:: filter(*args, **kwargs) - provides a django-like syntax for building a query. - The key difference between ``filter`` and ``where`` is that ``filter`` - supports traversing joins using django's "double-underscore" syntax:: - - >>> sq = SelectQuery(Entry).filter(blog__title='Some Blog') - - This method is chainable:: - - >>> base_q = User.filter(active=True) - >>> some_user = base_q.filter(username='charlie') + :param args: a list of :py:class:`Q` or :py:class:`Node` objects + :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :rtype: a :py:class:`SelectQuery` instance -.. py:method:: get(self, *args, **kwargs) + Provides a django-like syntax for building a query. + The key difference between :py:meth:`~SelectQuery.filter` and :py:meth:`~SelectQuery.where` is that ``filter`` + supports traversing joins using django's "double-underscore" syntax: + + .. code-block:: python + + >>> sq = SelectQuery(Entry).filter(blog__title='Some Blog') + + This method is chainable: + + .. code-block:: python + + >>> base_q = User.filter(active=True) + >>> some_user = base_q.filter(username='charlie') - :param args: a list of ``Q`` or ``Node`` objects - :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + .. py:method:: get(*args, **kwargs) - get a single row from the database that matches the given query. raises a - ``.DoesNotExist`` if no rows are returned:: - - >>> active = User.select().where(active=True) - >>> try: - ... user = active.get(username=username, password=password) - ... except User.DoesNotExist: - ... user = None - - this method is also expose via the model api:: - - >>> user = User.get(username=username, password=password) + :param args: a list of :py:class:`Q` or :py:class:`Node` objects + :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :rtype: :py:class:`Model` instance or raises ``DoesNotExist`` exception + + Get a single row from the database that matches the given query. Raises a + ``.DoesNotExist`` if no rows are returned: + + .. code-block:: python + + >>> active = User.select().where(active=True) + >>> try: + ... user = active.get(username=username, password=password) + ... except User.DoesNotExist: + ... user = None + + This method is also exposed via the :py:class:`Model` api: + + >>> user = User.get(username=username, password=password) -.. py:method:: where(self, *args, **kwargs) + .. py:method:: where(*args, **kwargs) - :param args: a list of ``Q`` or ``Node`` objects - :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :param args: a list of :py:class:`Q` or :py:class:`Node` objects + :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :rtype: a :py:class:`SelectQuery` instance - calling ``where()`` will act on the model that is currently the ``query context``. - Unlike ``filter()``, only columns from the current query context are exposed:: - - >>> sq = SelectQuery(Blog).where(title='some title', author=some_user) - >>> sq = SelectQuery(Blog).where(Q(title='some title') | Q(title='other title')) + Calling ``where()`` will act on the model that is currently the ``query context``. + Unlike :py:meth:`~SelectQuery.filter`, only columns from the current query context are exposed:: - .. note:: - - ``where()`` is chainable - -.. py:method:: join(self, model, join_type=None, on=None) + >>> sq = SelectQuery(Blog).where(title='some title', author=some_user) + >>> sq = SelectQuery(Blog).where(Q(title='some title') | Q(title='other title')) + + .. note:: + + :py:meth:`~SelectQuery.where` calls are chainable - :param model: the model to join on. there must be a ``ForeignKeyField`` between - the current "query context" and the model passed in. - :param join_type: allows the type of JOIN used to be specified explicitly - :param on: if multiple foreign keys exist between two models, this parameter - is a string containing the name of the ForeignKeyField to join on. + .. py:method:: join(model, join_type=None, on=None) - generate a JOIN clause from the current "query context" to the ``model`` passed - in, and establishes ``model`` as the new "query context". - - >>> sq = SelectQuery(Blog).join(Entry).where(title='Some Entry') - >>> sq = SelectQuery(User).join(Relationship, on='to_user_id').where(from_user=self) + :param model: the model to join on. there must be a :py:class:`ForeignKeyField` between + the current ``query context`` and the model passed in. + :param join_type: allows the type of ``JOIN`` used to be specified explicitly + :param on: if multiple foreign keys exist between two models, this parameter + is a string containing the name of the ForeignKeyField to join on. + :rtype: a :py:class:`SelectQuery` instance -.. py:method:: switch(self, model) + Generate a ``JOIN`` clause from the current ``query context`` to the ``model`` passed + in, and establishes ``model`` as the new ``query context``. + + >>> sq = SelectQuery(Blog).join(Entry).where(title='Some Entry') + >>> sq = SelectQuery(User).join(Relationship, on='to_user_id').where(from_user=self) - switches the "query context" to the given model. raises an exception if the - model has not been selected or joined on previously. + .. py:method:: switch(model) - >>> sq = SelectQuery(Blog).join(Entry).switch(Blog).where(title='Some Blog') + :param model: model to switch the ``query context`` to. + :rtype: a :py:class:`SelectQuery` instance -.. py:method:: count(self) + Switches the ``query context`` to the given model. Raises an exception if the + model has not been selected or joined on previously. + + >>> sq = SelectQuery(Blog).join(Entry).switch(Blog).where(title='Some Blog') - returns an integer representing the number of rows in the current query - - >>> sq = SelectQuery(Blog) - >>> sq.count() - 45 # <-- number of blogs - >>> sq.where(status=DELETED) - >>> sq.count() - 3 # <-- number of blogs that are marked as deleted + .. py:method:: count() -.. py:method:: exists(self) + :rtype: an integer representing the number of rows in the current query + + >>> sq = SelectQuery(Blog) + >>> sq.count() + 45 # <-- number of blogs + >>> sq.where(status=DELETED) + >>> sq.count() + 3 # <-- number of blogs that are marked as deleted - returns a boolean whether the current query will return any rows. uses an - optimized lookup, so use this rather than ``get``:: - - >>> sq = User.select().where(active=True) - >>> if sq.where(username=username, password=password).exists(): - ... authenticated = True + .. py:method:: exists() -.. py:method:: annotate(self, related_model, aggregation=None) + :rtype: boolean whether the current query will return any rows. uses an + optimized lookup, so use this rather than :py:meth:`~SelectQuery.get`. + + .. code-block:: python + + >>> sq = User.select().where(active=True) + >>> if sq.where(username=username, password=password).exists(): + ... authenticated = True - annotate a query with an aggregation performed on a related model, for example, - "get a list of blogs with the number of entries on each":: + .. py:method:: annotate(related_model, aggregation=None) - >>> Blog.select().annotate(Entry) - - if ``aggregation`` is None, it will default to ``Count(related_model, 'count')``, - but can be anything:: - - >>> blog_with_latest = Blog.select().annotate(Entry, Max('pub_date', 'max_pub')) - - .. note:: - - if the ``ForeignKeyField`` is ``nullable``, then a ``LEFT OUTER`` join - will be used, otherwise the join is an ``INNER`` join. if an ``INNER`` - join is used, in the above example blogs with no entries would not be - returned. to avoid this, you can explicitly join before calling ``annotate()``:: - - >>> Blog.select().join(Entry, 'left outer').annotate(Entry) + :param related_model: related :py:class:`Model` on which to perform aggregation, + must be linked by :py:class:`ForeignKeyField`. + :param aggregation: the type of aggregation to use, e.g. ``Max('pub_date', 'max_pub')`` + :rtype: :py:class:`SelectQuery` -.. py:method:: group_by(self, clause) + Annotate a query with an aggregation performed on a related model, for example, + "get a list of blogs with the number of entries on each":: + + >>> Blog.select().annotate(Entry) + + if ``aggregation`` is None, it will default to ``Count(related_model, 'count')``, + but can be anything:: + + >>> blog_with_latest = Blog.select().annotate(Entry, Max('pub_date', 'max_pub')) + + .. note:: + + If the ``ForeignKeyField`` is ``nullable``, then a ``LEFT OUTER`` join + will be used, otherwise the join is an ``INNER`` join. If an ``INNER`` + join is used, in the above example blogs with no entries would not be + returned. To avoid this, you can explicitly join before calling ``annotate()``:: + + >>> Blog.select().join(Entry, 'left outer').annotate(Entry) + + .. py:method:: group_by(clause) + + :param clause: either a single field name or a list of field names, in + which case it takes its context from the current query_context. it can + *also* be a model class, in which case all that models fields will be + included in the ``GROUP BY`` clause + :rtype: :py:class:`SelectQuery` + + .. code-block:: python + + >>> # get a list of blogs with the count of entries each has + >>> sq = Blog.select({ + ... Blog: ['*'], + ... Entry: [Count('id')] + ... }).group_by('id').join(Entry) + + >>> # slightly more complex, get a list of blogs ordered by most recent pub_date + >>> sq = Blog.select({ + ... Blog: ['*'], + ... Entry: [Max('pub_date', 'max_pub_date')], + ... }).join(Entry) + >>> # now, group by the entry's blog id, followed by all the blog fields + >>> sq = sq.group_by('blog_id').group_by(Blog) + >>> # finally, order our results by max pub date + >>> sq = sq.order_by(peewee.desc('max_pub_date')) + + .. py:method:: having(clause) + + :param clause: Expression to use as the ``HAVING`` clause + :rtype: :py:class:`SelectQuery` + + .. code-block:: python + + >>> sq = Blog.select({ + ... Blog: ['*'], + ... Entry: [Count('id', 'num_entries')] + ... }).group_by('id').join(Entry).having('num_entries > 10') - clause can be either a single field name or a list of field names, in - which case it takes its context from the current query_context. it can - *also* be a model class, in which case all that models fields will be - included in the GROUP BY clause - - :: - - >>> # get a list of blogs with the count of entries each has - >>> sq = Blog.select({ - ... Blog: ['*'], - ... Entry: [Count('id')] - ... }).group_by('id').join(Entry) - - >>> # slightly more complex, get a list of blogs ordered by most recent pub_date - >>> sq = Blog.select({ - ... Blog: ['*'], - ... Entry: [Max('pub_date', 'max_pub_date')], - ... }).join(Entry) - >>> # now, group by the entry's blog id, followed by all the blog fields - >>> sq = sq.group_by('blog_id').group_by(Blog) - >>> # finally, order our results by max pub date - >>> sq = sq.order_by(peewee.desc('max_pub_date')) - -.. py:method:: having(self, clause) - - adds the clause to the HAVING clause + .. py:method:: order_by(clause) - >>> sq = Blog.select({ - ... Blog: ['*'], - ... Entry: [Count('id', 'num_entries')] - ... }).group_by('id').join(Entry).having('num_entries > 10') + :param clause: Expression to use as the ``ORDER BY`` clause, see notes below + :rtype: :py:class:`SelectQuery` + + + .. note:: + Adds the provided clause (a field name or alias) to the query's + ``ORDER BY`` clause. If a field name is passed in, it must be a field on the + current ``query context``, otherwise it is treated as an alias. peewee also + provides two convenience methods to allow ordering ascending or descending, + called ``asc()`` and ``desc()``. + + example: + + .. code-block:: python + + >>> sq = Blog.select().order_by('title') + >>> sq = Blog.select({ + ... Blog: ['*'], + ... Entry: [Max('pub_date', 'max_pub')] + ... }).join(Entry).order_by(desc('max_pub')) + + check out how the ``query context`` applies to ordering: + + .. code-block:: python + + >>> blog_title = Blog.select().order_by('title').join(Entry) + >>> print blog_title.sql()[0] + SELECT t1.* FROM blog AS t1 + INNER JOIN entry AS t2 + ON t1.id = t2.blog_id + ORDER BY t1.title + + >>> entry_title = Blog.select().join(Entry).order_by('title') + >>> print entry_title.sql()[0] + SELECT t1.* FROM blog AS t1 + INNER JOIN entry AS t2 + ON t1.id = t2.blog_id + ORDER BY t2.title # <-- note that it's using the title on Entry this time + + .. py:method:: paginate(page_num, paginate_by=20) + + :param page_num: a 1-based page number to use for paginating results + :param paginate_by: number of results to return per-page + :rtype: :py:class:`SelectQuery` + + applies a ``LIMIT`` and ``OFFSET`` to the query. + + .. code-block:: python + + >>> Blog.select().order_by('username').paginate(3, 20) # <-- get blogs 41-60 -.. py:method:: order_by(self, clause) - - adds the provided clause (a field name or alias) to the query's - ORDER BY clause. if a field name is passed in, it must be a field on the - current "query context", otherwise it is treated as an alias. peewee also - provides two convenience methods to allow ordering ascending or descending, - called ``asc()`` and ``desc()``. - - example:: - - >>> sq = Blog.select().order_by('title') - >>> sq = Blog.select({ - ... Blog: ['*'], - ... Entry: [Max('pub_date', 'max_pub')] - ... }).join(Entry).order_by(desc('max_pub')) - - check out how the query context applies to ordering:: - - >>> blog_title = Blog.select().order_by('title').join(Entry) - >>> print blog_title.sql()[0] - SELECT t1.* FROM blog AS t1 - INNER JOIN entry AS t2 - ON t1.id = t2.blog_id - ORDER BY t1.title - - >>> entry_title = Blog.select().join(Entry).order_by('title') - >>> print entry_title.sql()[0] - SELECT t1.* FROM blog AS t1 - INNER JOIN entry AS t2 - ON t1.id = t2.blog_id - ORDER BY t2.title # <-- note that it's using the title on Entry this time - -.. py:method:: paginate(self, page_num, paginate_by=20) - - applies a LIMIT and OFFSET to the query. - - >>> Blog.select().order_by('username').paginate(3, 20) # <-- get blogs 41-60 + .. py:method:: distinct() -.. py:method:: distinct(self) + :rtype: :py:class:`SelectQuery` - indicates that this query should only return distinct rows. results in a - SELECT DISTINCT query. + indicates that this query should only return distinct rows. results in a + ``SELECT DISTINCT`` query. -.. py:method:: execute(self) + .. py:method:: execute() + + :rtype: :py:class:`QueryResultWrapper` - executes the query and returns a ``QueryResultWrapper`` for iterating over - the result set. the results are managed internally by the query and whenever - a clause is added that would possibly alter the result set, the query is - marked for re-execution. + Executes the query and returns a :py:class:`QueryResultWrapper` for iterating over + the result set. The results are managed internally by the query and whenever + a clause is added that would possibly alter the result set, the query is + marked for re-execution. -.. py:method:: __iter__(self) + .. py:method:: __iter__() - executes the query:: - - >>> for user in User.select().where(active=True): - ... print user.username + Executes the query: + + .. code-block:: python + + >>> for user in User.select().where(active=True): + ... print user.username UpdateQuery ----------- -``UpdateQuery`` is fairly straightforward and is used for updating rows in the -database. +.. py:class:: UpdateQuery -.. py:method:: __init__(self, model, **kwargs) + Used for updating rows in the database. - creates an ``UpdateQuery`` instance for the given model. "kwargs" is a dictionary - of field: value pairs:: + .. py:method:: __init__(model, **kwargs) + + :param model: :py:class:`Model` class on which to perform update + :param kwargs: mapping of field/value pairs containing columns and values to update + + .. code-block:: python + + >>> uq = UpdateQuery(User, active=False).where(registration_expired=True) + >>> print uq.sql() + ('UPDATE user SET active=? WHERE registration_expired = ?', [0, 1]) - >>> uq = UpdateQuery(User, active=False).where(registration_expired=True) - >>> print uq.sql() - ('UPDATE user SET active=? WHERE registration_expired = ?', [0, 1]) + .. py:method:: where(*args, **kwargs) -.. py:method:: execute(self) + :param args: a list of :py:class:`Q` or :py:class:`Node` objects + :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :rtype: a :py:class:`UpdateQuery` instance - performs the query, returning the number of rows that were updated + .. note:: + + :py:meth:`~UpdateQuery.where` calls are chainable + + .. py:method:: execute() + + :rtype: Number of rows updated + + Performs the query DeleteQuery ----------- -``DeleteQuery`` deletes rows of the given model. It will *not* traverse -foreign keys or ensure that constraints are obeyed, so use it with care. +.. py:class:: DeleteQuery + + Deletes rows of the given model. + + .. note:: + It will *not* traverse foreign keys or ensure that constraints are obeyed, so use it with care. -.. py:method:: __init__(self, model) + .. py:method:: __init__(model) - creates a ``DeleteQuery`` instance for the given model:: + creates a ``DeleteQuery`` instance for the given model: + + .. code-block:: python + + >>> dq = DeleteQuery(User).where(active=False) + >>> print dq.sql() + ('DELETE FROM user WHERE active = ?', [0]) - >>> dq = DeleteQuery(User).where(active=False) - >>> print dq.sql() - ('DELETE FROM user WHERE active = ?', [0]) + .. py:method:: where(*args, **kwargs) + + :param args: a list of :py:class:`Q` or :py:class:`Node` objects + :param kwargs: a mapping of column + lookup to value, e.g. "age__gt=55" + :rtype: a :py:class:`DeleteQuery` instance + + .. note:: + + :py:meth:`~DeleteQuery.where` calls are chainable -.. py:method:: execute(self) + .. py:method:: execute() + + :rtype: Number of rows deleted - performs the query, returning the number of rows that were deleted + Performs the query InsertQuery ----------- -``InsertQuery`` creates a new row for the given model. +.. py:class:: InsertQuery -.. py:method:: __init__(self, model, **kwargs) + Creates a new row for the given model. - creates an ``InsertQuery`` instance for the given model where kwargs is a - dictionary of field name to value:: - - >>> iq = InsertQuery(User, username='admin', password='test', active=True) - >>> print iq.sql() - ('INSERT INTO user (username, password, active) VALUES (?, ?, ?)', ['admin', 'test', 1]) + .. py:method:: __init__(model, **kwargs) -.. py:method:: execute(self) + creates an ``InsertQuery`` instance for the given model where kwargs is a + dictionary of field name to value: + + .. code-block:: python + + >>> iq = InsertQuery(User, username='admin', password='test', active=True) + >>> print iq.sql() + ('INSERT INTO user (username, password, active) VALUES (?, ?, ?)', ['admin', 'test', 1]) + + .. py:method:: execute() + + :rtype: primary key of the new row - performs the query, returning the primary key of the row that was added + Performs the query RawQuery -------- -``RawQuery`` allows execution of an arbitrary SELECT query and returns instances -of the model via a ``QueryResultsWrapper``. +.. py:class:: RawQuery -.. py:method:: __init__(self, model, query, *params) + Allows execution of an arbitrary ``SELECT`` query and returns instances + of the model via a :py:class:`QueryResultsWrapper`. - creates a ``RawQuery`` instance for the given model which, when executed, - will run the given query with the given parameters and return model instances:: - - >>> rq = RawQuery(User, 'SELECT * FROM users WHERE username = ?', 'admin') - >>> for obj in rq.execute(): - ... print obj - + .. py:method:: __init__(model, query, *params) -.. py:method:: execute(self) + creates a ``RawQuery`` instance for the given model which, when executed, + will run the given query with the given parameters and return model instances:: + + >>> rq = RawQuery(User, 'SELECT * FROM users WHERE username = ?', 'admin') + >>> for obj in rq.execute(): + ... print obj + + + .. py:method:: execute() + + :rtype: a :py:class:`QueryResultWrapper` for iterating over the result set. The results are instances of the given model. - executes the query and returns a ``QueryResultWrapper`` for iterating over - the result set. the results are instances of the given model. + Performs the query