Вы находитесь на странице: 1из 9

26/03/14

Chapter 5. Building a Wiki Djen of Django

Djen of Django
CHAPTER 5. BUILDING A WIKI
A wiki application:
In this chapter, we will build a wiki from scratch. Basic functionality includes: User registration Article Management (CRUD) with ReST support Audit trail for articles Revision history

searchsite

Search

Table Of Contents
Chapter 1: Introduction Chapter 2. Building a personal CD library. Chapter 3. Building a Pastebin. Chapter 4. Building a Blog Chapter 5. Building a Wiki
A wiki application: Reusable Apps: Article Management:

Reusable Apps:
To manage user registrations, we will use djangoregistration. You can download it from Note http://bitbucket.org/ubernostrum/djangoregistration/ djangoregistration is a great example of a reusable app, which can be customized to fit our requirements while providing the most common pattern by default (sign up, email activation etc) Some functionality offered by the app: User signup view Activation email view Validate activation key and create user account Login, logout from contrib.auth Management scripts to clear expired registrations We shall follow the default pattern, i.e. user registration with activation email in the wiki app, although djangoregistration allows customization of the process by using backends which should know how to handle the registration. It ships with two such backends: default and simple Note browse through the code of djangoregistration to see what urls are avaialbe, what context is passed to the templates, which urls are mapped to which views etc.

Chapter 6. Building a Yahoo Answers like site Chapter 7. Building a Project management application Chapter 8. Building a Social news Site

Using Django with Appengine Django for the PHP programmer Django for the JSP/Sevelet programmer. Hacking Django internals.

This Page

Looking at named urls from urls.py would be useful for creating links to registration, login etc by using Show Source the url templatetag. To install, download the app and run:
p y t h o ns e t u p . p yi n s t a l l

This will be installed to the site wide python packages directory but can still be imported from our app since it is a python package. Now, include registration in your INSTALLED_APPS, do syncdb and include the urls:
f r o md j a n g o . c o n f . u r l s . d e f a u l t si m p o r t*

#U n c o m m e n tt h en e x tt w ol i n e st oe n a b l et h ea d m i n : f r o md j a n g o . c o n t r i bi m p o r ta d m i n a d m i n . a u t o d i s c o v e r ( )
u r l p a t t e r n s=p a t t e r n s ( ' ' , #E x a m p l e : #( r ' ^ d j e n _ p r o j e c t / ' ,i n c l u d e ( ' d j e n _ p r o j e c t . f o o . u r l s ' ) ) ,

agiliq.com/books/djenofdjango/chapter5.html

1/9

26/03/14

Chapter 5. Building a Wiki Djen of Django


#U n c o m m e n tt h ea d m i n / d o cl i n eb e l o wa n da d d' d j a n g o . c o n t r i b . a d m i n d o c s ' #t oI N S T A L L E D _ A P P St oe n a b l ea d m i nd o c u m e n t a t i o n : #( r ' ^ a d m i n / d o c / ' ,i n c l u d e ( ' d j a n g o . c o n t r i b . a d m i n d o c s . u r l s ' ) ) , #U n c o m m e n tt h en e x tl i n et oe n a b l et h ea d m i n : ( r ' ^ a c c o u n t s / ' ,i n c l u d e ( ' r e g i s t r a t i o n . b a c k e n d s . d e f a u l t . u r l s ' ) ) , ( r ' ^ a d m i n / ' ,i n c l u d e ( a d m i n . s i t e . u r l s ) ) , ( r ' ^ p a s t e b i n / ' ,i n c l u d e ( ' p a s t e b i n . u r l s ' ) ) , ( r ' ^ b l o g / ' ,i n c l u d e ( ' b l o g . u r l s ' ) ) ,

Note djangoregistration provides views for login at accounts/login so we can omit our previous entry for the same. The app requires a setting called ACCOUNT_ACTIVATION_DAYS which is the number of days before which the user should complete registration. If you are not using local_settings.py, create one and add from local_settings import * to settings.py. Now add this setting to local_settings.py:
#D j a n g or e g i s t r a t i o ns e t t i n g s A C C O U N T _ A C T I V A T I O N _ D A Y S=7

Now, accounts/register/ provides the user signup view and renders to registration/registration_form.html, so lets write the template:
{ %e x t e n d s" r e g i s t r a t i o n / b a s e . h t m l "% } { %c o m m e n t% } * * r e g i s t r a t i o n / r e g i s t r a t i o n _ f o r m . h t m l * * U s e dt os h o wt h ef o r mu s e r sw i l lf i l lo u tt or e g i s t e r .B yd e f a u l t ,h a s t h ef o l l o w i n gc o n t e x t :

` ` f o r m ` ` T h er e g i s t r a t i o nf o r m .T h i sw i l lb ea ni n s t a n c eo fs o m es u b c l a s s o f` ` d j a n g o . f o r m s . F o r m ` ` ;c o n s u l t` D j a n g o ' sf o r m sd o c u m e n t a t i o n < h t t p : / / d o c s . d j a n g o p r o j e c t . c o m / e n / d e v / t o p i c s / f o r m s / > ` _f o r i n f o r m a t i o no nh o wt od i s p l a yt h i si nat e m p l a t e . { %e n d c o m m e n t% }


{ %b l o c kc o n t e n t% } < h 1 > C r e a t en e wa c c o u n t < / h 1 > < f o r ma c t i o n = " "m e t h o d = " P O S T " > { %c s r f _ t o k e n% } < t a b l e > { {f o r m . a s _ t a b l e} } < / t a b l e > < i n p u tt y p e = " s u b m i t "n a m e = " s u b m i t "v a l u e = " S u b m i t " > < / f o r m > { %e n d b l o c k% }

Note that form is the user signup form passed as context by register of djangoregistration. To demostrate template heirarchy, we have used a base template and built all other registration templates on top of it. The base template looks like: wiki/templates/registration/base.html
< ! D O C T Y P EH T M L > < h t m l > < h e a d > < m e t ah t t p e q u i v = " c o n t e n t t y p e "c o n t e n t = " t e x t / h t m l ;c h a r s e t = u t f 8 " > < t i t l e > W i k i < / t i t l e > { %b l o c ke x t r a _ h e a d% } { %e n d b l o c k% } < / h e a d > < b o d y > < d i vc l a s s = " c o n t e n t " > < p > { %b l o c kc o n t e n t% } { %e n d b l o c k% } < / p > < / d i v > < / b o d y > < / h t m l >

At the moment, we have extra_head and content blocks. You can place as many blocks as you like with careful planning and hierarchy. For example extra_head would serve to include child template specific css/scripts. Global css/scripts could be directly included in base.html to make them available to all child templates. (e.g. something general like jquery.js would go in base while something specific

agiliq.com/books/djenofdjango/chapter5.html

2/9

26/03/14

Chapter 5. Building a Wiki Djen of Django

like jquery.form.js would go in the child template) Note Templates outside any subdirectory are considered harmful since they may interfere with templates from other applications. In general it is better to namespace your templates by putting them inside subdirectories. E.g.: wiki/templates/base.html Wrong! wiki/templates/wiki/base.html Right. The reason being that templates from other apps extending base.html would find both wiki/templates/blog/base.html and wiki/templates/base.html. Then you would be left at the mercy of precedence of TEMPLATE_LOADERS to get the blog base template and not the wiki base template. Of course, it can be useful if used correctly, but quite hard to debug if not. At this point the user can submit a signup form. He will be sent an email with subject from wiki/templates/registration/activation_email_subject.text and content from wiki/templates/registration/activation_email.txt. Lets write these templates: A nice base email template would be wiki/templates/registration/email.txt:
H i ! , { %b l o c kb o d y% } { %e n d b l o c k% } R e g a r d s , A d m i n N o t e :T h i si sa na u t o g e n e r a t e dm a i l ,p l e a s ed o n ' tr e p l y .

In wiki/templates/registration/activation_email_subject.txt
{ %c o m m e n t% } * * r e g i s t r a t i o n / a c t i v a t i o n _ e m a i l _ s u b j e c t . t x t * *

U s e dt og e n e r a t et h es u b j e c tl i n eo ft h ea c t i v a t i o ne m a i l .B e c a u s et h e s u b j e c tl i n eo fa ne m a i lm u s tb eas i n g l el i n eo ft e x t ,a n yo u t p u t f r o mt h i st e m p l a t ew i l lb ef o r c i b l yc o n d e n s e dt oas i n g l el i n eb e f o r e b e i n gu s e d .T h i st e m p l a t eh a st h ef o l l o w i n gc o n t e x t : ` ` a c t i v a t i o n _ k e y ` ` T h ea c t i v a t i o nk e yf o rt h en e wa c c o u n t . ` ` e x p i r a t i o n _ d a y s ` ` T h en u m b e ro fd a y sr e m a i n i n gd u r i n gw h i c ht h ea c c o u n tm a yb e a c t i v a t e d . ` ` s i t e ` ` A no b j e c tr e p r e s e n t i n gt h es i t eo nw h i c ht h eu s e rr e g i s t e r e d ; d e p e n d i n go nw h e t h e r` ` d j a n g o . c o n t r i b . s i t e s ` `i si n s t a l l e d ,t h i s m a yb ea ni n s t a n c eo fe i t h e r` ` d j a n g o . c o n t r i b . s i t e s . m o d e l s . S i t e ` ` ( i ft h es i t e sa p p l i c a t i o ni si n s t a l l e d )o r ` ` d j a n g o . c o n t r i b . s i t e s . m o d e l s . R e q u e s t S i t e ` `( i fn o t ) .C o n s u l t` t h e d o c u m e n t a t i o nf o rt h eD j a n g os i t e sf r a m e w o r k < h t t p : / / d o c s . d j a n g o p r o j e c t . c o m / e n / d e v / r e f / c o n t r i b / s i t e s / > ` _f o r d e t a i l sr e g a r d i n gt h e s eo b j e c t s 'i n t e r f a c e s . { %e n d c o m m e n t% }


Y o u ra c c o u n ta c t i v a t i o nd e t a i l sa t{ {s i t e} }

In wiki/templates/registration/activation_email.txt
{ %e x t e n d s" r e g i s t r a t i o n / e m a i l . t x t "% } { %c o m m e n t% } * * r e g i s t r a t i o n / a c t i v a t i o n _ e m a i l . t x t * *

U s e dt og e n e r a t et h eb o d yo ft h ea c t i v a t i o ne m a i l .S h o u l dd i s p l a ya l i n kt h eu s e rc a nc l i c kt oa c t i v a t et h ea c c o u n t .T h i st e m p l a t eh a st h e f o l l o w i n gc o n t e x t : ` ` a c t i v a t i o n _ k e y ` ` T h ea c t i v a t i o nk e yf o rt h en e wa c c o u n t .

agiliq.com/books/djenofdjango/chapter5.html

3/9

26/03/14

Chapter 5. Building a Wiki Djen of Django

` ` e x p i r a t i o n _ d a y s ` ` T h en u m b e ro fd a y sr e m a i n i n gd u r i n gw h i c ht h ea c c o u n tm a yb e a c t i v a t e d . ` ` s i t e ` ` A no b j e c tr e p r e s e n t i n gt h es i t eo nw h i c ht h eu s e rr e g i s t e r e d ; d e p e n d i n go nw h e t h e r` ` d j a n g o . c o n t r i b . s i t e s ` `i si n s t a l l e d ,t h i s m a yb ea ni n s t a n c eo fe i t h e r` ` d j a n g o . c o n t r i b . s i t e s . m o d e l s . S i t e ` ` ( i ft h es i t e sa p p l i c a t i o ni si n s t a l l e d )o r ` ` d j a n g o . c o n t r i b . s i t e s . m o d e l s . R e q u e s t S i t e ` `( i fn o t ) .C o n s u l t` t h e d o c u m e n t a t i o nf o rt h eD j a n g os i t e sf r a m e w o r k < h t t p : / / d o c s . d j a n g o p r o j e c t . c o m / e n / d e v / r e f / c o n t r i b / s i t e s / > ` _f o r d e t a i l sr e g a r d i n gt h e s eo b j e c t s 'i n t e r f a c e s . { %e n d c o m m e n t% }


{ %b l o c kb o d y% } P l e a s ef o l l o wt h el i n kt oa c t i v a t ey o u ra c c o u n t . h t t p : / / { {s i t e} } { %u r lr e g i s t r a t i o n _ a c t i v a t ea c t i v a t i o n _ k e y% } { %e n d b l o c k% }

Note the use of url templatetag to get the activation link. Also, the tag returns a relative url, so we use the site context variable passed by the register view Note If you have a mail server configured, well and good. If not, you could use gmails smtp server by adding
#D j a n g or e g i s t r a t i o ns e t t i n g s A C C O U N T _ A C T I V A T I O N _ D A Y S=7 #E m a i ls e t t i n g sf o rs e n d i n ga c c o u ta c t i v a t i o nm a i l s E M A I L _ U S E _ T L S=T r u e E M A I L _ H O S T=" s m t p . g m a i l . c o m " E M A I L _ H O S T _ U S E R=" u s e r @ e x a m p l e . c o m " E M A I L _ H O S T _ P A S S W O R D=" s e c r e t " E M A I L _ P O R T=5 8 7

to local_settings.py Some other templates required by djangoregistration:


{ %e x t e n d s" r e g i s t r a t i o n / b a s e . h t m l "% } { %c o m m e n t% } * * r e g i s t r a t i o n / a c t i v a t e . h t m l * *

U s e di fa c c o u n ta c t i v a t i o nf a i l s .W i t ht h ed e f a u l ts e t u p ,h a st h ef o l l o w i n gc o n t e x t : ` ` a c t i v a t i o n _ k e y ` ` T h ea c t i v a t i o nk e yu s e dd u r i n gt h ea c t i v a t i o na t t e m p t . { %e n d c o m m e n t% }
{ %b l o c kc o n t e n t% } S o r r y ,y o u ra c c o u n tc o u l dn o tb ea c t i v a t e da tt h i st i m e . { %e n d b l o c k% } { %e x t e n d s" r e g i s t r a t i o n / b a s e . h t m l "% } { %c o m m e n t% } * * r e g i s t r a t i o n / a c t i v a t i o n _ c o m p l e t e . h t m l * *

U s e da f t e rs u c c e s s f u la c c o u n ta c t i v a t i o n .T h i st e m p l a t eh a sn oc o n t e x t v a r i a b l e so fi t so w n ,a n ds h o u l ds i m p l yi n f o r mt h eu s e rt h a tt h e i r a c c o u n ti sn o wa c t i v e . { %e n d c o m m e n t% }
{ %b l o c kc o n t e n t% } T h a n k s !Y o u ra c c o u n th a sb ea c t i v a t e d .P l e a s e< ah r e f = " { %u r la u t h _ l o g i n% } " > l o g i nt oc o n t i n u e < / a > { %e n d b l o c k% } { %e x t e n d s" r e g i s t r a t i o n / b a s e . h t m l "% } { %c o m m e n t% } * * r e g i s t r a t i o n / r e g i s t r a t i o n _ c o m p l e t e . h t m l * *

U s e da f t e rs u c c e s s f u lc o m p l e t i o no ft h er e g i s t r a t i o nf o r m .T h i s t e m p l a t eh a sn oc o n t e x tv a r i a b l e so fi t so w n ,a n ds h o u l ds i m p l yi n f o r m t h eu s e rt h a ta ne m a i lc o n t a i n i n ga c c o u n t a c t i v a t i o ni n f o r m a t i o nh a s b e e ns e n t . { %e n d c o m m e n t% }
{ %b l o c kc o n t e n t% }

agiliq.com/books/djenofdjango/chapter5.html

4/9

26/03/14

Chapter 5. Building a Wiki Djen of Django

A na c c o u n ta c t i v a t i o ne m a i lh a sb e e ns e n tt oy o u .P l e a s ec h e c ky o u re m a i la n df o l l o wt h ei n s t r u c t i o n s . { %e n d b l o c k% }

At this point, a user should be able to signup, get the activation email, follow the activation link, complete registration and login. All this by just writing down the templates. Amazing, isnt it? Now you would have noticed that the logged in user is redirected to /accounts/profile. We would next customize the wiki app and redirect the user to the index page.

Article Management:
This is similar to our last app (blog) in many ways. Significant changes would be: Allow any registered user to add/edit an article(instead of just the administrator). Allow ReST input instead of just plain text. Keep track of all edit sessions related to an article. To demonstrate custom model managers, we would like to show only published articles on the index page. Lets write down the models:
f r o md j a n g o . d bi m p o r tm o d e l s f r o md j a n g o . c o n t r i b . a u t h . m o d e l si m p o r tU s e r f r o md j a n g o . t e m p l a t e . d e f a u l t f i l t e r si m p o r ts l u g i f y

#C r e a t ey o u rm o d e l sh e r e .
c l a s sP u b l i s h e d A r t i c l e s M a n a g e r ( m o d e l s . M a n a g e r ) : d e fg e t _ q u e r y _ s e t ( s e l f ) : r e t u r ns u p e r ( P u b l i s h e d A r t i c l e s M a n a g e r ,s e l f ) . g e t _ q u e r y _ s e t ( ) . f i l t e r ( i s _ p u b l i s h e d = T r u e ) c l a s sA r t i c l e ( m o d e l s . M o d e l ) : " " " R e p r e s e n t saw i k ia r t i c l e " " " t i t l e=m o d e l s . C h a r F i e l d ( m a x _ l e n g t h = 1 0 0 ) s l u g=m o d e l s . S l u g F i e l d ( m a x _ l e n g t h = 5 0 ,u n i q u e = T r u e ) t e x t=m o d e l s . T e x t F i e l d ( h e l p _ t e x t = " F o r m a t t e du s i n gR e S T " ) a u t h o r=m o d e l s . F o r e i g n K e y ( U s e r ) i s _ p u b l i s h e d=m o d e l s . B o o l e a n F i e l d ( d e f a u l t = F a l s e ,v e r b o s e _ n a m e = " P u b l i s h ? " ) c r e a t e d _ o n=m o d e l s . D a t e T i m e F i e l d ( a u t o _ n o w _ a d d = T r u e ) o b j e c t s=m o d e l s . M a n a g e r ( ) p u b l i s h e d=P u b l i s h e d A r t i c l e s M a n a g e r ( ) d e f_ _ u n i c o d e _ _ ( s e l f ) : r e t u r ns e l f . t i t l e d e fs a v e ( s e l f ,* a r g s ,* * k w a r g s ) : i fn o ts e l f . s l u g : s e l f . s l u g=s l u g i f y ( s e l f . t i t l e ) s u p e r ( A r t i c l e ,s e l f ) . s a v e ( * a r g s ,* * k w a r g s ) @ m o d e l s . p e r m a l i n k d e fg e t _ a b s o l u t e _ u r l ( s e l f ) : r e t u r n( ' w i k i _ a r t i c l e _ d e t a i l ' ,( ) ,{' s l u g ' :s e l f . s l u g} ) c l a s sE d i t ( m o d e l s . M o d e l ) : " " " S t o r e sa ne d i ts e s s i o n " " " a r t i c l e=m o d e l s . F o r e i g n K e y ( A r t i c l e ) e d i t o r=m o d e l s . F o r e i g n K e y ( U s e r ) e d i t e d _ o n=m o d e l s . D a t e T i m e F i e l d ( a u t o _ n o w _ a d d = T r u e ) s u m m a r y=m o d e l s . C h a r F i e l d ( m a x _ l e n g t h = 1 0 0 ) c l a s sM e t a : o r d e r i n g=[ ' e d i t e d _ o n ' ] d e f_ _ u n i c o d e _ _ ( s e l f ) : r e t u r n" % s-% s-% s "% ( s e l f . s u m m a r y ,s e l f . e d i t o r ,s e l f . e d i t e d _ o n ) @ m o d e l s . p e r m a l i n k d e fg e t _ a b s o l u t e _ u r l ( s e l f ) : r e t u r n( ' w i k i _ e d i t _ d e t a i l ' ,s e l f . i d )

Most of the code should be familiar, some things that are new:

agiliq.com/books/djenofdjango/chapter5.html

5/9

26/03/14

Chapter 5. Building a Wiki Djen of Django

The Article model will hold all articles, but only those with is_published set to True will be displayed on the front page. We have a defined a custom model manager called PublishedArticlesManager which is a queryset that only returns the published articles. Nonpublished articles would be used only for editing. So, we retain the default model manager by setting objects to models.Manager Now, to fetch all articles, one would use Articles.objects.all, while Artilces.published.all would return only published articles. A custom manager should subclass models.Manager and define the custom get_query_set property. The Edit class would hold an edit session by a registered user on an article. We see the use of verbose_name and help_text keyword arguments. By default, django will replace _ with spaces and Capitalize the field name for the label. This can be overridden using verbose_name argument. help_text will be displayed below a field in the rendered ModelForm The ordering attribute of meta class for Edit defines the default ordering in which edits will be returned. This can also be done using order_by in the queryset. Now, we will need urls similar to our previous app, plus we would need a url to see the article history.
f r o md j a n g o . c o n f . u r l s . d e f a u l t si m p o r t* f r o mm o d e l si m p o r tA r t i c l e u r l p a t t e r n s=p a t t e r n s ( ' ' , u r l ( r ' ^ $ ' , ' d j a n g o . v i e w s . g e n e r i c . l i s t _ d e t a i l . o b j e c t _ l i s t ' , { ' q u e r y s e t ' :A r t i c l e . p u b l i s h e d . a l l ( ) , } , n a m e = ' w i k i _ a r t i c l e _ i n d e x ' ) , u r l ( r ' ^ a r t i c l e / ( ? P < s l u g > [ \ w ] + ) $ ' , ' d j a n g o . v i e w s . g e n e r i c . l i s t _ d e t a i l . o b j e c t _ d e t a i l ' , { ' q u e r y s e t ' :A r t i c l e . o b j e c t s . a l l ( ) , } , n a m e = ' w i k i _ a r t i c l e _ d e t a i l ' ) , u r l ( r ' ^ h i s t o r y / ( ? P < s l u g > [ \ w ] + ) $ ' , ' w i k i . v i e w s . a r t i c l e _ h i s t o r y ' , n a m e = ' w i k i _ a r t i c l e _ h i s t o r y ' ) , u r l ( r ' ^ a d d / a r t i c l e $ ' , ' w i k i . v i e w s . a d d _ a r t i c l e ' , n a m e = ' w i k i _ a r t i c l e _ a d d ' ) , u r l ( r ' ^ e d i t / a r t i c l e / ( ? P < s l u g > [ \ w ] + ) $ ' , ' w i k i . v i e w s . e d i t _ a r t i c l e ' , n a m e = ' w i k i _ a r t i c l e _ e d i t ' ) , )

Note that: We will use the list_detail generic views for the article index page and detail page. We have to autofill the author to the loggedin user, so will write a custom view for that. Similarly, it would be better to write down custom views for edit article and article history pages. Here are the forms we will need:
f r o md j a n g oi m p o r tf o r m s f r o mm o d e l si m p o r tA r t i c l e ,E d i t c l a s sA r t i c l e F o r m ( f o r m s . M o d e l F o r m ) : c l a s sM e t a : m o d e l=A r t i c l e e x c l u d e=[ ' a u t h o r ' ,' s l u g ' ]

c l a s sE d i t F o r m ( f o r m s . M o d e l F o r m ) : c l a s sM e t a : m o d e l=E d i t f i e l d s=[ ' s u m m a r y ' ]

Here: We are excluding author and slug which will be autofilled. We are inluding the summary field in Edit model only. The other fields (article, editor, edited_on)

agiliq.com/books/djenofdjango/chapter5.html

6/9

26/03/14
will be autofilled. In our custom views:
#C r e a t ey o u rv i e w sh e r e .

Chapter 5. Building a Wiki Djen of Django

f r o md j a n g o . c o n t r i b . a u t h . d e c o r a t o r si m p o r tl o g i n _ r e q u i r e d f r o md j a n g o . c o n t r i bi m p o r tm e s s a g e s f r o md j a n g o . s h o r t c u t si m p o r tr e d i r e c t ,r e n d e r _ t o _ r e s p o n s e ,g e t _ o b j e c t _ o r _ 4 0 4 f r o md j a n g o . t e m p l a t ei m p o r tR e q u e s t C o n t e x t f r o md j a n g o . v i e w s . g e n e r i c . l i s t _ d e t a i li m p o r to b j e c t _ l i s t f r o mm o d e l si m p o r tA r t i c l e ,E d i t f r o mf o r m si m p o r tA r t i c l e F o r m ,E d i t F o r m @ l o g i n _ r e q u i r e d d e fa d d _ a r t i c l e ( r e q u e s t ) : f o r m=A r t i c l e F o r m ( r e q u e s t . P O S To rN o n e ) i ff o r m . i s _ v a l i d ( ) : a r t i c l e=f o r m . s a v e ( c o m m i t = F a l s e ) a r t i c l e . a u t h o r=r e q u e s t . u s e r a r t i c l e . s a v e ( ) m s g=" A r t i c l es a v e ds u c c e s s f u l l y " m e s s a g e s . s u c c e s s ( r e q u e s t ,m s g ,f a i l _ s i l e n t l y = T r u e ) r e t u r nr e d i r e c t ( a r t i c l e ) r e t u r nr e n d e r _ t o _ r e s p o n s e ( ' w i k i / a r t i c l e _ f o r m . h t m l ' , {' f o r m ' :f o r m} , c o n t e x t _ i n s t a n c e = R e q u e s t C o n t e x t ( r e q u e s t ) ) @ l o g i n _ r e q u i r e d d e fe d i t _ a r t i c l e ( r e q u e s t ,s l u g ) : a r t i c l e=g e t _ o b j e c t _ o r _ 4 0 4 ( A r t i c l e ,s l u g = s l u g ) f o r m=A r t i c l e F o r m ( r e q u e s t . P O S To rN o n e ,i n s t a n c e = a r t i c l e ) e d i t _ f o r m=E d i t F o r m ( r e q u e s t . P O S To rN o n e ) i ff o r m . i s _ v a l i d ( ) : a r t i c l e=f o r m . s a v e ( ) i fe d i t _ f o r m . i s _ v a l i d ( ) : e d i t=e d i t _ f o r m . s a v e ( c o m m i t = F a l s e ) e d i t . a r t i c l e=a r t i c l e e d i t . e d i t o r=r e q u e s t . u s e r e d i t . s a v e ( ) m s g=" A r t i c l eu p d a t e ds u c c e s s f u l l y " m e s s a g e s . s u c c e s s ( r e q u e s t ,m s g ,f a i l _ s i l e n t l y = T r u e ) r e t u r nr e d i r e c t ( a r t i c l e ) r e t u r nr e n d e r _ t o _ r e s p o n s e ( ' w i k i / a r t i c l e _ f o r m . h t m l ' , { ' f o r m ' :f o r m , ' e d i t _ f o r m ' :e d i t _ f o r m , ' a r t i c l e ' :a r t i c l e , } , c o n t e x t _ i n s t a n c e = R e q u e s t C o n t e x t ( r e q u e s t ) ) d e fa r t i c l e _ h i s t o r y ( r e q u e s t ,s l u g ) : a r t i c l e=g e t _ o b j e c t _ o r _ 4 0 4 ( A r t i c l e ,s l u g = s l u g ) r e t u r n o b j e c t _ l i s t ( r e q u e s t , q u e r y s e t = E d i t . o b j e c t s . f i l t e r ( a r t i c l e _ _ s l u g = s l u g ) , e x t r a _ c o n t e x t = { ' a r t i c l e ' :a r t i c l e } )

We are using the login_required decorator to only allow loggedin users to add/edit articles. get_object_or_404 is a shortcut method which gets an object based on some criteria. While the get method throws an DoesNotExist when no match is found, this method automatically issues a 404 Not Found response. This is useful when getting an object based on url parameters (slug, id etc.) redirect, as we have seen, would issue a HttpResponseRedirect on the article's get_absolute_url property. edit_article includes two forms, one for the Article model and the other for the Edit model. We save both the forms one by one. Passing instance to the form will populate existing data in the fields. As planned, the author field of article and editor, article fields of Article and Edit respectively, are filled up before commiting save. article_history view first checks if an article with the given slug exists. If yes, it forwards the request to the object_list generic view. We also pass the article from the generic view using extra_context. Note the filter on the Edit models queryset and the lookup on the related Article's slug. To display all the articles on the index page: wiki/templates/wiki/article_list.html:

agiliq.com/books/djenofdjango/chapter5.html

7/9

26/03/14
{ %i fo b j e c t _ l i s t% } < h 2 > R e c e n tA r t i c l e s < / h 2 >

Chapter 5. Building a Wiki Djen of Django

< u l > { %f o ra r t i c l ei no b j e c t _ l i s t% } < l i > < ah r e f = " { %u r lw i k i _ a r t i c l e _ d e t a i la r t i c l e . s l u g% } " > { {a r t i c l e . t i t l e} } < / a > < / l i > { %e n d f o r% } < / u l > { %e l s e% } < h 2 > N oa r t i c l e sh a v eb e e np u b l i s h e dy e t . < / h 2 > { %e n d i f% } < ah r e f = " { %u r lw i k i _ a r t i c l e _ a d d% } " > C r e a t en e wa r t i c l e < / a >

We will include links to edit and view history in the article detail page: wiki/templates/wiki/article_detail.html:
{ %l o a dm a r k u p% } { %i fm e s s a g e s% } < d i vc l a s s = " m e s s a g e s " > < u l > { %f o rm e s s a g ei nm e s s a g e s% } < l ic l a s s = " { {m e s s a g e . t a g} } " > { {m e s s a g e} } < / l i > { %e n d f o r% } < / u l > < / d i v > { %e n d i f% } { %i fn o to b j e c t . i s _ p u b l i s h e d% } < l a b e l > N o t e :T h i sa r t i c l eh a sn o tb e e np u b l i s h e dy e t < / l a b e l > { %e n d i f% } < h 2 > { {o b j e c t . t i t l e} } < / h 2 > < p > { {o b j e c t . t e x t | r e s t r u c t u r e d t e x t} } < / p > < h 3 > A c t i o n s < h 3 > < u l > < l i > < ah r e f = " { %u r lw i k i _ a r t i c l e _ e d i to b j e c t . s l u g% } " > E d i tt h i sa r t i c l e < / a > < / l i > < l i > < ah r e f = " { %u r lw i k i _ a r t i c l e _ h i s t o r yo b j e c t . s l u g% } " > V i e wa r t i c l eh i s t o r y < / a > < / l i > < / u l > < ah r e f = " { %u r lw i k i _ a r t i c l e _ i n d e x% } " > S e eA l l < / a >

Here we are using the restructuredtext filter provided by django.contrib.markup. To use this, you will need to add django.contrib.markup to INSTALLED_APPS and use the load templatetag to load markup filters. Note You will require docutils for ReST markup to work. Get it from: http://docutils.sourceforge.net/ Heres the form that would be used to create/edit an article: wiki/templates/wiki/article_form.html
{ %i fa r t i c l e% } < h 1 > E d i ta r t i c l e{ {a r t i c l e} } < / h 1 > { %e l s e% } < h 1 > C r e a t en e wa r t i c l e < / h 1 > { %e n d i f% } < f o r ma c t i o n = " "m e t h o d = " P O S T " > { %c s r f _ t o k e n% } < t a b l e > { {f o r m . a s _ t a b l e} } { {e d i t _ f o r m . a s _ t a b l e} } < / t a b l e > < i n p u tt y p e = " s u b m i t "n a m e = " s u b m i t "v a l u e = " S u b m i t " >

agiliq.com/books/djenofdjango/chapter5.html

8/9

26/03/14
< / f o r m >

Chapter 5. Building a Wiki Djen of Django

Note that the same form is used for add article and edit article pages. We pass the article context variable from edit page, so we can use it to identify if this is an add or edit page. We also render the edit_form passed from edit page. Rendering an undefined variable does not throw any error in the template, so this works fine in the add page. The article history template: wiki/templates/wiki/edit_list.html
< h 2 > H i s t o r y < / h 2 > < h 3 > { {a r t i c l e} } < / h 3 > < t a b l eb o r d e r = " 1 "c e l l s p a c i n g = " 0 " > < t h e a d > < t h > E d i t e d < / t h > < t h > U s e r < / t h > < t h > S u m m a r y < / t h > < / t h e a d > < t b o d y > { %f o re d i ti no b j e c t _ l i s t% } < t r > < t d > { {e d i t . e d i t e d _ o n} } < / t d > < t d > { {e d i t . e d i t o r} } < / t d > < t d > { {e d i t . s u m m a r y} } < / t d > < / t r > { %e n d f o r% } < t r > < t d > { {a r t i c l e . c r e a t e d _ o n} } < / t d > < t d > { {a r t i c l e . a u t h o r} } < / t d > < t d > N e wa r t i c l ec r e a t e d < / t d > < / t r > < / t b o d y > < / t a b l e > < b r/ > < ah r e f = " { %u r lw i k i _ a r t i c l e _ d e t a i la r t i c l e . s l u g% } " > < <B a c k < / a >

Displays a table with the history. Since we are done with our templates, let us redirect our logged in users to the wiki index page:
{ %e x t e n d s" r e g i s t r a t i o n / b a s e . h t m l "% } { %c o m m e n t% } * * r e g i s t r a t i o n / a c t i v a t i o n _ c o m p l e t e . h t m l * *

U s e da f t e rs u c c e s s f u la c c o u n ta c t i v a t i o n .T h i st e m p l a t eh a sn oc o n t e x t v a r i a b l e so fi t so w n ,a n ds h o u l ds i m p l yi n f o r mt h eu s e rt h a tt h e i r a c c o u n ti sn o wa c t i v e . { %e n d c o m m e n t% }
{ %b l o c kc o n t e n t% } T h a n k s !Y o u ra c c o u n th a sb ea c t i v a t e d .P l e a s e< ah r e f = " { %u r la u t h _ l o g i n% } ? n e x t = { %u r lw i k i _ a r t i c l e _ i n d e x% } " > l o g i nt oc o n t i n u e < / a > { %e n d b l o c k% }

previous | next | index


Contact Us
EMAIL : hello@agiliq.com ADDRESS :

Know More
Follow us on Twitter Connect us on Facebook Linkedin Youtube - Python Screencasts Our Posterous Our Tumblr Code

Read
Blog Newsletter Django Design Patterns Djen of Django Django Gotchas Software Consulting Howto

About
Home Contact Us Who we are What we do Blog Forum

Agiliq Info Solutions India Pvt Ltd, # 302, Siri Sampada Appts, Near Madhapur Police Station, Hyderabad - 500033. India.

Copyright 2010, Agiliq. Created using Sphinx 1.1.3.

agiliq.com/books/djenofdjango/chapter5.html

9/9

Вам также может понравиться