Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- = Template Tags Cheat Sheet =
- == Inheritance and Inclusion ==
- * {% extends "base.html "%}
- * {% block content %} ... {% endblock %} or {% block content %} ... {% endblock content %}
- * {% include "foo/bar.html" %}
- * {% ssi /home/html/ljworld.com/includes/right_generic.html %}
- == Filters and Tags ==
- * {% load <tag_or_filter_lib> %}
- * {% filter <filter>[|<filter>...] %}
- == Control ==
- * {% for o in some_list %} ... {% endfor %}
- * {% ifchanged %} (Content to check status of) {% endifchanged %}
- * {% if <var> %} ... {% endif %}
- * {% if[not]equal user.id comment.user_id %} ... [{% else %}] ... {% endif[not]equal %}
- * {% cycle row1,row2 %}
- * {% cycle row1,row2 as rowcolors %}
- ** {% cycle rowcolors %}
- * {% firstof var1 var2 var3 %}
- * {% regroup people by gender as grouped %} ... {% for group in grouped %} {{ group.grouper }} ...
- == Others ==
- * {% templatetag (openblock|closeblock|openvariable|closevariable|openbrace|closebrace) %}
- * {% widthratio this_value max_value 100 %}
- * It is {% now "jS F Y H:i" %}
- * {% spaceless %} ... {% endspaceless %}
- django_db_cheat.txtPage 1 of 2 Aug/2008
- 1#Model of question:2
- class
- Blog(models.Model): 3 name=models.CharField(max_length=100) 4 tagline=models.TextField() 5 bestEntry=ForeignKey("mutually_referential.Entry") 6# two way relations usually illegal unless specified as referential7# myId = models.AutoField(primary_key=True)8# by default the primary key is called 'id' - but overridable9# 'blog.pk' to will refer to the primary key regardless of what its called1011
- class
- Author(models.Model): 12 name=models.CharField(max_length=50) 13 email=models.EmailField() 1415
- class
- Entry(models.Model): 16 blog=models.ForeignKey(Blog) 17# automagically in any Blog instance b, there is now a set called b.entry_set18# blog = models.ForeignKey(Blog, related_name='custom_name')19# (this overrides default name of blog.entry_set with blog.custom_name)20 headline=models.CharField(max_length=255) 21 body_text=models.TextField() 22 pub_date=models.DateTimeField() 23 authors=models.ManyToManyField(Author) 24
- class
- Meta:25 ordering= ('-pub_date', 'headline') 26
- def
- articles_from_same_day(self): 27
- return
- Entry.objects.filter(pub_date=self.pub_date).exclude(id=self.id) 28# a.articles_from_same_day() returns a resultset2930
- class
- EntryDetail(models.Model): 31 entry=models.OneToOneField(Entry) 32 details=models.TextField() 3334#Inserting35 b=Blog(name='Beatles Blog',tagline='All the latest Beatles news.') 36 b.save() 37 bb=Blog(id=3,name='Not Cheddar',tagline='Anything but cheese.') 38 bb.save() # Overrides the previous blog with ID=3! 3940#Manager41 Entry.objects.get(id=2) # gets id=2 42 e=Entry.objects.select_related().get(id=2) 43
- print
- e.blog# Doesn't hit the database twice; uses cached version. 4445#Filter and Exclusion - it is a lazy operation, not done until iterated/read46 Entry.objects.filter(pub_date__year=2006) 47 Entry.objects.filter( 48 headline__startswith='What').exclude( 49 pub_date__gte=datetime.now()).filter( 50 pub_date__gte=datetime(2005, 1, 1)) 51 Entry.objects.filter(blog=b) # Query using object instance 52 Entry.objects.filter(blog=b.id) # Query using id from instance 53 Entry.objects.filter(blog=5) # Query using id directly 5455#Sorting56 Entry.objects.order_by('pub_date', '-headline') 5758# Deleting59 e.delete() #If there are dependencies, they will be deleted too 60 Entry.objects.filter(pub_date__year=2005).delete() 61# warning - when deleting a resultset, you might delete many entries6263# Updating64 e.headline= 'Everything is the same' 65 e.save()
- - 1 -
- django_db_cheat.txtPage 2 of 2 Aug/2008
- 66 Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same') 67 Entry.objects.all().update(blog=b) 68# need to save if you use bulk69
- for
- item
- in
- Entry.objects.all(): 70 item.save() 7172# Many-to-One - via ForeignKey73 b=Blog.objects.get(id=1) 74 b.entry_set.all() # Returns all Entry objects related to Blog. 75 b.entry_set.filter(headline__contains='Lennon') 76 b.entry_set.count() 77 e=Entry.objects.get(id=234) 78 b.entry_set.add(e) # Associates Entry e with Blog b. 79 e=b.entry_set.create(headline='Hello',body_text='Hi',pub_date=datetime.date(2005, 1, 1)) 80 b.entry_set.remove(e) # Disassociates Entry e from Blog b. 81 b.entry_set.clear() 82 b.entry_set= [e1,e2] # You can't set to one element need [e1,] 8384# One-to-One - via OneToOneField85 ed=EntryDetail.objects.get(id=2) 86 ed.entry# Returns the related Entry object. 87 e=Entry.objects.get(id=2) 88 e.entrydetail89 e.entrydetail=ed 9091# Many-to-Many - via ManyToManyField92 e=Entry.objects.get(id=3) 93 e.authors.all() # R
- eturns all Author objects for this Entry.94 e.authors.filter(name__contains='John') 95 a=Author.objects.get(id=5) 96 a.entry_set.all() # Returns all Entry objects for this Author. 97 a.entry_set.filter(body_text__contains='John') 98 a.entry_set.create(....) #just like normal create 99100# Misc functions101 Author.objects.get_or_create(name='David',defaults={'email': 'generic@email.com'}) 102103# Returning a url to go to object:104# suppose your urlconf had105# (r'/archive/(?P<year>\d{4})/(?P<month>\d{1,2})/(?P<day>\d{1,2})/$', archive_view)106
- from
- django.db.models
- import
- permalink107
- def
- get_absolute_url(self): 108
- return
- ('archive_view', (), { 109'year':self.created.year, 110'month':self.created.month, 111'day':self.created.day}) 112 get_absolute_url=permalink(get_absolute_url) 113114# Doing custom sql:115
- def
- my_custom_sql(self): 116
- from
- django.db
- import
- connection117 cursor=connection.cursor() 118 cursor.execute("SELECT foo FROM bar WHERE baz = %s", [self.baz]) 119 row=cursor.fetchone() 120
- return
- row121122# Field Type Reference123 AutoField,BooleanField,CharField,CommaSeparatedIntegerField,DateField,DateTimeField, 124 DecimalField,EmailField,FileField,FilePathField,FloatField,ImageField,IntegerField, 125 IPAddressField,NullBooleanField,PhoneNumberField,PositiveIntegerField, 126 PositiveSmallIntegerField,SlugField,SmallIntegerField,TextField,TimeField,URLField, 127 USStateField,XMLField128129# Model Methods130 __unicode__,get_absolute_url
- - 2 -
- Quick and simple urlpatterns code examples for django cheatsheet
- See more
- django_url_cheat.txtPage 1 of 2 Aug/2008
- 1
- from
- django.conf.urls.defaults
- import
- *2 urlpatterns=patterns('', 3# (regular exp, function, optional dictionary, optional name)4# Doesn’t differentiate request method, all go to the same function5(r'^articles/2003/$', 'news.views.special_case_2003'), 6# "/articles/2003" -> no match need "2003/"7(r'^articles/(\d{4})/$', 'news.views.year_archive'), 8# ordering matters9# "/articles/2003/" -> news.views.special_case_2003, not news.views.year_archive(2003)10(r'^articles/special/(?P<year>\d{4})/$', 'news.views.year_archive'), 11# "/articles/special/2003" -> news.views.year_archive(request, year='2003')12(r'^articles/(\d{4})/(\d{2})/$', 'news.views.month_archive'), 13(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'news.views.article_detail'), 14# "/articles/2003/03/3/" -> news.views.article_detail(request, '2003', '03', '3')15)16 urlpatterns+=patterns('sports.views', # append like a list 17(r'^sports/2003/$', 'some_function'), 18# "/sports/2003/" -> sports.views.some_function(request)19)2021# Generic actions are useful if you are doing something generic such as:22# by default {'extra_context':{}}, add more context into extras if necessary23 urlpatterns+=patterns('django.views.generic.simple', 24(r'^page_new/(?P<id>\d+)/$', 'direct_to_template', {'template': 'page_detail.html'}),) 25 urlpatterns+=patterns('django.views.generic.simple', 26(r'^page/(?P<id>\d+)/$', 'redirect_to', {'url': '/page_new/%(id)s/'}},) 27 urlpatterns+=patterns('django.views.generic.list_detail', 28(r'^page/all/$', 'object_list', {'queryset':Pages.objects.all() }),) 29# default: {'paginate_by':'infinity' , 'page':'1',30# 'template_name':'app/model_list.html' }31 urlpatterns+=patterns('django.views.generic.list_detail', 32(r'^page/all/(?P<id>\d+)/$', 'object_detail', {'queryset':Pages.objects.all(), 'object _id':id}),) 33# default: {'paginate_by':'infinity' , 'page':'1',34# 'template_name':'app/model_detail.html' }35 urlpatterns+=patterns('django.views.generic.create_update', 36(r'^...$', 'create_object', {'model':SomeModel
- or
- 'form_class':SomeForm}),) 37# default: {'post_save_redirect':object.get_absolute_url(), 'login_required':False,38# 'template_name':'app/model_form.html' }39 urlpatterns+=patterns('django.views.generic.create_update', 40(r'^...$', 'update_object', {'model': / 'form_class':, 'object_id':SomeID}),) 41# default: {'post_save_redirect':object.get_absolute_url(), 'login_required':False,42# 'template_name':'app/model_form.html' }43 urlpatterns+=patterns('django.views.generic.create_update', 44(r'^...$', 'delete_object', {'model': / 'form_class':, 'object_id':SomeID}),) 45# default: {'post_save_redirect':object.get_absolute_url(), 'login_required':False,46# 'template_name':'app/model_confirm_delete.html' }4748# Parents are good for subdividing the work49 urlpatterns+=patterns('', # use include to add child url matchers: 50(r'^weblog/(?P<idName>\w+)/',include('mysite.app.url')), 51)52# in file app/url.py:53
- from
- django.conf.urls.defaults
- import
- *54 urlpatterns=patterns('app.views', 55(r'^$', 'blog.index'), 56# "/weblog/me/" -> app.views.blog.index(request, idName='me')57(r'^post/(?P<postIndex>\d+)$', 'post.show'), 58# "/weblog/me/12" -> app.views.post.show(request, idName='me', postIndex='12')59(r'^details/$', 'blog.details', {'extraData', 'foo!'}) 60# "/weblog/details/" -> app.views.blog.details(request, idName='me', extraData='foo!')61(r'^post/(?P<pid>\d+)/comment/(?P<cid>\d+)/$', 'post.show', {'gotoComment', 'true'}, "w eblog-viewComment"),62# "/weblog/post/1/comment/1/" -> app.views.blog.details(request, idName='me', pid='1',cid='1', gotoComment='true')
- - 1 -
- django_url_cheat.txtPage 2 of 2 Aug/2008
- 63# the template tag {% url weblog-viewComment pid=1,cid=1 %} returns "/weblog/post/1/comment/1/"64)6566# often you will write one function which has a default parameter to save code:67 urlpatterns=patterns('app.views', 68(r'^$', 'blog.index'), 69(r'^/(?P<postIndex>\d+)/$', 'blog.index')) 70
- def
- index(request,postIndex='1') 71....7273# often we want to find a url that will execute a function with some parameters74# we would use {% url function args %} in a template. in code we would use:75
- from
- django.core.urlresolvers
- import
- reverse76 reverse(viewname,urlconf=
- None
- ,args=
- None
- ,kwargs=
- None
- )77
- def
- myview(request): 78
- return
- HttpResponseRedirect(reverse('weblog-viewComment',args='pid=1,cid=1')) 7980# regular reference:81# . any char82# ^ start of string $ end of string83# * 0 or more of preceding + 1 or more of preceding84# ? 0 or 1 of preceding (?!..) matches when it doesnt match ..85# *? 0 or more, minimal match +? 1 or more, minimal match86# {m} exactly m of preceding {m,n} between m to n of preceding87# [..] eg. [abc],[a-z],[0-9a-z] [^..] matches if doesn't match [..]88# (..) groups what's inside (?=..) matches .. but doesn't consume it89# \d [0-9] (decimal digit) \D [^0-9] (non-digit)90# \w [a-zA-Z0-9_] (alphanumeric) \W [^a-zA-Z0-9_] (non-alphanumeric)91# \s [ \t\n\r\f\v] (whitespace) \S [^ \t\n\r\f\v] (non-whitespace)9293# Request and Response Object94
- def
- index(request,index='1') 95 request.path# /weblog/me/ 96 request.method# either 'GET', 'POST', 'HEAD', ... 97 request.GET['someVarName'] # whatever it should be 98 request.GET['someVarName', 'default'] # if it doesn't exist then default. also for POST 99 request.POST['someVarName'] 100 request.R
- EQUEST['someName'] # searches GET then FILES 101 request.COOKIES['attributeName'] 102 request.FILES['someFilename'] # request.POST does not have files 103# includes methods: read(num_bytes=...), chunk() and attrs: file_name, file_size104 request.META['someMetaName'] 105# includes: CONTENT_LENGTH, CONTENT_TYPE, HTTP_ACCEPT_ENCODING, SERVER_PORT,106# HTTP_ACCEPT_LANGUAGE, HTTP_HOST, HTTP_REFERER, HTTP_USER_AGENT,107# QUERY_STR
- ING, R
- EMOTE_ADDR, R
- EMOTE_HOST, R
- EQUEST_METHOD, SERVER_NAME,108 request.user# object: django.contrib.auth.models.User 109 request.get_full_path() # includes any stuff after the last directory / 110 request.build_absolute_uri() # includes a http://www.. bit that is read from their side 111 request.is_ajax() # major ajax libraries send a signal that a query is for ajax
- See also [http://www.djangoproject.com/documentation/templates/ templates for designers].
- django_forms_cheat.txtPage 1 of 2 Aug/2008
- 1# Simple Form with standard widgets.2
- from
- django
- import
- forms3
- class
- ContactForm(forms.Form): 4 subject=forms.CharField(max_length=100) 5 message=forms.CharField() 6 sender=forms.EmailField() 7 cc_myself=forms.BooleanField(required=
- False
- ) # required=True by default89# Form with non-standard widget used10
- class
- CommentForm(forms.Form): 11 name=forms.CharField(widget=forms.TextInput(attrs={'class':'special'})) 12 url=forms.URLField() 13 comment=forms.CharField(widget=forms.TextInput(attrs={'size':'40'})) 1415# Using forms16 f=ContactForm() # unbound - no data 17 data= {'subject': 'hello', 18'message': 'Hi there', 19'sender': 'foo@example.com', 20'cc_myself':
- True
- ,21'extra_field': 'foo'} 22 f=ContactForm(data) # bound - has data 23 f.is_valid() # validate 24 f.cleaned_data# returns a dictionary of the data - note: extra_field is dropped 2526# outputting html data all outputs automatically give labelled ids27 f.as_p() # renders as paragraphs 28 f.as_ul() # renders as dot-points (no <ul>) 29 f.as_table() 3031# Mapping forms to models32
- class
- BlogForm(forms.ModelForm): 33# name = CharField(widget=forms.TextInput()) # you can override any field if you wish34
- class
- Meta:35 model=Blog# where Article is something that inherits model.Model 36 fields= ('name',) # 'fields' specifies visible fields - so can't edit tagline, etc 37# exclude = ('tagline', 'bestEntry') # this also works, chose 'fields' or 'exclude'3839 form1=BlogForm() # just like a normal form. all normal form methods available 40 myblog=Blog.objects.get(id=1) 41 form2=BlogForm(instance=myblog) # just like normal binding 42 form2.save() # generate a model, then inserts it into db, or updates db 43# creating a new blog via a form44 b=Blog() 45 form3=BlogForm(request.POST,instance=b) 46 new_blog=form3.save(commit=
- False
- ) # generates a model, but doesn't insert into db47 new_blog.tagline= ... # since the form excluded this field, it must be filled before savin g48 new_blog.save() # saves the generated model 49 f.save_m2m() # saves many-to-many data - this is required only if you have commit=False 5051# Sample use of forms:52
- def
- contact(request): 53
- if
- request.method== 'POST': 54 form=ContactForm(request.POST,request.FILES) 55# the second parameter is optional56
- if
- form.is_valid(): 57# Do form processing here...58
- return
- HttpResponseR
- edirect('/url/on_success/') 59
- else
- :60 form=ContactForm() 61
- return
- render_to_response('contact.html', {'form':form}) 6263# you can write your own forms if you really want to:64# see django_template_cheat.txt
- - 1 -
- django_forms_cheat.txtPage 2 of 2 Aug/2008
- 65<form method="post"action=""> 66<dl> 67{%
- for
- field
- in
- form%}68<dt>{{field.label_tag}}{%
- if
- field.field.required%}*{%endif%}</dt> 69<dd>{{field}}</dd> 70{%
- if
- field.help_text%}<dd>{{field.help_text}}</dd>{%endif%} 71{%
- if
- field.errors%}<dd
- class
- ="myerrors">{{field.errors}}</dd>{%endif%} 72{%endfor%} 73</dl> 74<input type="submit" /> 75</form> 7677# Writing custom widgets means supplying a render function78
- from
- django.template.loader
- import
- render_to_string79
- class
- CalendarWidget(forms.Widget): 80
- def
- render(self,name,value,attrs=
- None
- ):81
- return
- render_to_string("widget/CalendarWidget.html",locals()) 82
- class
- Media:83 css= {'all': ('pretty.css',)} 84 js= ('animations.js', 'actions.js') 8586
- class
- BirthdayForm(forms.Form): 87 name=forms.CharField(max_length=100) 88 birthday=forms.DateField(widget=CalendarWidget) 8990# Field Type Reference91 BooleanField,CharField,ChoiceField,DateField,DateTimeField,DecimalField, 92 EmailField,FileField,FilePathField,FloatField,ImageField,IntegerField, 93 IPAddressField,MultipleChoiceField,NullBooleanField,RegexField,TimeField, 94 URLField9596# Field Argument Reference97 required,label,initial,widget,help_text,error_messages 9899# Widget Type Reference100 TextInput,PasswordInput,HiddenInput,FileInput,DateTimeInput,Textarea, 101 CheckboxInput,Select,SelectMultiple,RadioSelect,CheckboxSelectMultiple, 102 SplitDateTimeWidget
- django templates cheat sheet
- Ratings: (1)|Views: 12,906|Likes: 58
- Published by windoze007
- A concise 3 page cheat sheet of django's templates
- See more
- django_template_cheat.txtPage 1 of 3 Aug/2008
- 1######################Rendition of django html templates2
- from
- django.template
- import
- Context,Template3 t=Template("My name is {{ my_name }}.") 4 c=Context({"my_name": "adrian"}) 5 t.render(c) # outputs "My name is Adrian" 6 c=Context({"my_name": "dolores"}) 7 t.render(c) # outputs "My name is dolores" 89 t=Template("My name is {{ person.first_name }}.") 10# when render meets a "." it will first try person["first_name"]11 c=Context({"person": {"first_name": "Joe", "last_name": "Johnson"}}) 12# then person.first_name13
- class
- PersonClass:
- pass
- 14 p=PersonClass() 15 p.first_name= "Joe" 16 c=Context({"person":p}) 17# then person.first_name()18
- class
- PersonClass2:19
- def
- first_name(self): 20
- return
- "Joe"21 p=PersonClass2() 22 c=Context({"person":p}) 23# then person[first_name]24 c=Context({"person": ["Samantha", "Ron", "Joe"], 'first_name': 2}) 2526 c=Context({"name": "david"}) 27 c['name'] 28
- del
- c['name'] 29 c['name']="jack" 3031#often you want modules to automatically add some variables into your context:32
- from
- django.template
- import
- RequestContext33
- def
- extraProcessor(request): 34
- return
- {'misc_extra_dictonary': 'here'} 35 c=RequestContext(request, {'name': 'david',},extraProcessor) 36#c will be populated with extra vars37#it will read the settings.py for TEMPLATE_CONTEXT_PROCESSORS and execute them all38#modules are under django.core.context_processors39#include auth:supporting users, debug, i18n, media, request:include request in context40#any function that takes a HttpR
- equest and returns a dictionary can be used4142#often you want to load html pages:43
- from
- django.template.loader
- import
- get_template,select_template44 t=get_template(template_name) #load file or exception 45 t=select_template(template_name_list) #load first in list that exists 46#it will only load from TEMPLATE_DIRS in settings.py4748#often you want to save time not doing all this context, template stuff:49
- from
- django.template.loader
- import
- render_to_string50 rendered=render_to_string('my_template.html', { 'foo': 'bar' }) 5152#often you want to write your own filters and tags53#but right now you don't know what filters and tags are yet....5455######################Sample page base_generic.html56{# this is a comment #}5758{%load custom_module1 custom_module2%} 59{# loads custom python filter and tag modules#}6061<html> 62<head> 63<link rel="stylesheet"href="style.css" /> 64<title>{%block title%}David Website{%endblock%}</title> 65{# defining blocks will allow you to replace them later #}
- - 1 -
- django_template_cheat.txtPage 2 of 3 Aug/2008
- 66</head> 67<body> 68<div id="sidebar"> 69{%block sidebar%} 70<ul> 71<li><a href="/">Home</a></li> 72<li><a href="/blog/">Blog</a></li> 73</ul> 74{%endblock%} 75</div> 7677<div id="content"> 78{%block content%}{%endblock%} 79</div> 8081<div id="disclaimer"> 82{%block disclaimer%}Copyright David{%endblock david%} 83{%include"legals/extraDisclaimer.html" %} 84{# renders that html into this page, passes current context #}85</div> 8687{%
- if
- wantDebug%}88{%debug%}{%enddebug%} 89{%endif%} 90</body> 91</html> 9293######################Sample page extended.html94{%extends"base_generic.html" %} 95{# if you render this page, it will render the base_generic.html #}96{# with each block that is defined here replacing that of the parent #}9798{%load custom_module2%} 99{# note that if your parent loads a module, you don't have it #}100101{%block title%}{{section.title}}{%endblock%} 102{# replace the block called "title" in base_generic #}103{# reads variable called section from the context #}104{# searches section["title"],section.title,section.title(),section[title] #}105{# in that exact order #}106107{# unreplaced blocks from base_generic.html, such as 'sidebar' are kept #}108109{%block content%} 110{%spaceless%} {# removes useless whitespace from the html below #} 111<h1>{{section.title}}</h1> 112{%
- for
- story
- in
- story_list%} 113<h2 color:{%cycle'red' 'blue' %}> 114{# each time the tag is read, it will output the next item #}115{{forloop.counter}} 116{# prints the loop counter #}117{# also: counter0, revcounter/0, first, last, parentloop.counter) #}118<a href="{{ story.get_absolute_url }}"> 119{{story.headline|upper|capfirst}} 120{# the filter uppercases then captalises first letter #}121</a> 122</h2> 123<p>{{story.tease|cut:"\n"|truncatewords:"100" }}</p> 124{# the filter removes \n and then gets first 100 words #}125{{story.first_comment}} 126{# by default the variable is escaped #}127{# < > ' " & are converted into html character codes #}128129{{story.comment_list|first}} 130{{story.comment_list|join:", "}}
- - 2 -
- FERRIS STATE UNIVERSITY
- Tutorial
- This tutorial will walk you through creating a simple API. If you haven't yet, be sure to read through the :doc:`introduction` and :doc:`getting_started` section. This assumes you have already created a new project- if not, head back to :doc:`getting_started` for instructions.
- A Simple “Posts” Service
- To illustrate how quickly you can get a basic CRUD service up and running we'll create a simple "Posts" service. You could use service like this in a simple blog.
- First we will create a posts folder inside of the app folder.
- Note
- For a refresher on how the new folder structure works, take a look at the :doc:`introduction` again. Also recall that python requires all folders you create in your project will need an empty __init__.py file inside of it. (Note: when Google allows us to use Python 3, we’ll no longer have to do this, c'mon, Google!)
- Next we'll create the service file. The convention here is to use [service_name]_service.py, or in this case posts_service.py. By following the convention, Ferris' discovery utility will find this file and automatically load it for us. Inside this file we will define all the different methods we’d like our service to contain.
- Before we start making methods to interact with Posts, however, we’ll need an actual Post model. We can accomplish this in two ways:
- Create a separate models.py file that contains the models we need.
- Define the model inside our service file.
- For the purpose of simplicity and because our Posts model is relatively simple, we’re going to go with inline option. While it may seem to fly in the face of traditional MVC conventions, we feel there's no need to make this any more complicated.
- At this point you should have a file structure that looks like this:
- /app
- /posts
- /__init__.py
- /posts_service.py
- Inside of posts_service.py we’ll need to import Ferris' Model class and the ndb module using the following lines:
- from google.appengine.ext import ndb
- from ferris3 import Model
- Note
- Notice that we are importing ndb from the appengine module, not the ferris3 module. There is an ndb package inside the ferris3 module, but it’s not the one we want.
- Ferris’ module names match the modules they supplement. So ferris.ndb supplements `google.appengine.ext.ndb, ferris.messages supplements protorpc.messages, ferris.endpoints supplements `endpoints, etc.
- Now lets add our simple Posts model:
- class Post(Model):
- title = ndb.StringProperty()
- content = ndb.TextProperty()
- You'll notice that we're using App Engine's built-in ndb here- this is a pattern that permeates Ferris as we embrace and extend the underlying platform.
- Now that we have a model let's create service to expose an API that allows CRUD operations on our Post model. First we'll need to import a few more things from Ferris. Augment your import statement to include auto_service, Service, and hvild, so it looks something like this:
- from ferris3 import Model, Service, hvild, auto_service
- Service will be the base class for our service and auto_service is a decorator that is used to automatically register and expose our service with Google Cloud Endpoints. Putting that all together our class definition looks like this:
- @auto_service
- class PostsService(Service):
- Now we get to hvild. Hvild is one of the more "magical" parts of Ferris and it will allow you to generate CRUD API methods like insert, get, update, delete, etc. quickly and painlessly. Those familiar with Ferris 2 will find it to be very similar to "scaffold". Lets give our posts service some basic functionality with the following lines:
- list = hvild.list(Post)
- get = hvild.get(Post)
- delete = hvild.delete(Post)
- insert = hvild.insert(Post)
- update = hvild.update(Post)
- That's it, just set your methods equal to their hvild counterparts and pass in the model that the methods manipulate (in this case Post).
- To recap, our posts_service.py should look like this:
- from google.appengine.ext import ndb
- from ferris3 import Model, Service, hvild, auto_service
- class Post(Model):
- title = ndb.StringProperty()
- content = ndb.TextProperty()
- @auto_service
- class PostsService(Service):
- list = hvild.list(Post)
- get = hvild.get(Post)
- delete = hvild.delete(Post)
- insert = hvild.insert(Post)
- update = hvild.update(Post)
- There are is another hvild method which will take just an ounce more effort to use: paginated_list. The only difference is that along with the model you must also pass in a limit parameter which will be the number of entities that appear on each page of the results. In our case, let's include 3 posts per page by adding these lines:
- paginated_list = hvild.paginated_list(Post, limit=3)
- Using the Google APIs Explorer
- Now let's test these methods! First we're gonna need some posts in the datastore. We can put them there in one of two ways: We can either use the interactive console (located at localhost:8000) or we can use the insert method in the APIs Explorer that we just had hvild build for us. Either is fine, but we might as well use the Explorer just to get used to navigating through it.
- Note
- To get to the Explorer, navigate to http://localhost:8080/_ah/api/explorer. Remember, if you're using the launcher your ports may be different.
- From here you should see "ferris API" in your list of available endpoints. If it doesn't show up, take a trip over to your terminal or error console to see what the error is and try to resolve it. Hopefully if you're following this guide it shouldn't be anything more than a typo.
- Tip
- If you get stuck reach out to us via the mailing list. Ferris has a fantastic community!
- After clicking on "Ferris API", you will be taken to a new list showing all of the new services that we've just defined. Navigate to "ferris.posts.insert" to add some test posts.
- From here, click inside the "Request body" input field and you will be given the option to choose a new property type add data for. We gave the Post model title and content properties, so you should see those along with a id property. A id will be generated automatically so we do not need to manually define it. Just give your post a title and some content and click the blue "Execute" button. You should receive a 200 OK notice of success along with a copy of the JSON data that describes the post you have just created.
- Create a few posts, and then navigate back to the list of services and choose "ferris.posts.paginated_list". Ignore the fields for now and click "Execute". You should see some JSON showing some of the posts you made. If you made 4 or more, it will show only 3 of them, and after the third one it should give you a nextPageToken. This can be entered into the pageToken field to see the next page.
- Feel free to test some of the other services. Some of them will concern just one particular post and will require the id of an item. Use it to delete, edit, or get a post.
- A Little More Complexity
- So let's say you want to want to get a particular post but you don't know its key and all you remember about it is that its title was "Ferris 3 is Awesome". How would we create a service that allows us to get a post by its title? Unfortunately hvild cannot do this for us so we're going to have to write a few more than just a line or two. But don't fret! Ferris 3 will still make this a cinch!
- First let's go ahead and import the entire ferris3 module. It isn't necessary to rename it, but shortening it to f3 will make things just a tiny bit quicker for us in the long run:
- import ferris3 as f3
- Now we're going to use some of the methods inside of the f3 module to create a model message for the Post model. Bear with me on this one it's gonna be tough:
- PostMessage = f3.model_message(Post)
- Huh, turns out that was totally painless. Creating messages for ndb Models in Ferris 3 is actually this simple. Model and List messages can be made in a snap. It's also possible to reduce the amount of information that your message will contain using the exclude parameter which we'll demonstrate later. For now let's get back to our "get by title" method.
- Note
- For more information about protorpc and messages see Google's documentation
- When building a method we'll use a similar decorator as we did when we built the class:
- @f3.auto_method()
- auto_method takes a few optional arguments, namely returns and name. returns is the type of message that the service will return and name is the name that the service will appear under in the API explorer. If you leave out returns then ferris will just expect you to return nothing. If you leave out name ferris will just use the name of the function as the method name. In this case, we're going to return an instance of the PostMessage that we recently defined and we'll call our method get_by_title even though we could have left that out:
- @f3.auto_method(returns=PostMessage, name="get_by_title")
- Now we declare the method. We'll also name it get_by_title for consistency. The bare minimum parameters we need to give it are self and request. However, since we want to take in another parameter called title we'll need to add that as well. All together it should look like this:
- def get_by_title(self, request, title=(str,)):
- The syntax on the title parameter may look strange. Cloud endpoints needs to know the type of the parameter and this is our way of letting it know. The auto_method decorator will take care of the rest. We can also optionally give it a default value by doing title=(str, 'a default'), but in this case we want it to be a required field.
- What's next is to use the Ferris 3 toolchain to get the Post we want, convert it into a PostMessage, and finally return it. First we'll show the complete code then break it down line-by-line:
- query = Post.query(Post.title==title)
- post = query.get()
- if not post:
- raise f3.NotFoundException()
- if not post.key.kind() == 'Post':
- raise f3.InvalidRequestException()
- message = f3.messages.serialize(PostMessage, post)
- return message
- Let's break this down:
- The first thing we do is create a standard ndb query using Post.query(Post.title==title).
- Next we call query.get() which will fetch the first record from the query. This should be the post we're after.
- The two if statements are sanity checks. First, we make sure we actually got an item back from the query, secondly, we make sure the item is actually a Post. (The kind check isn't strictly necessary here, however, you'll want to make sure you do this for any methods that use the id to get an item directly).
- Finally, we'll serialize our Post object into a message using messages.serialize and return it.
- For reference, the final code for the tutorial is:
- from google.appengine.ext import ndb
- from ferris3 import Model, Service, hvild, auto_service
- import ferris3 as f3
- class Post(Model):
- title = ndb.StringProperty()
- content = ndb.TextProperty()
- PostMessage = f3.model_message(Post)
- @auto_service
- class PostsService(Service):
- list = hvild.list(Post)
- paginated_list = hvild.paginated_list(Post, limit=3)
- get = hvild.get(Post)
- delete = hvild.delete(Post)
- insert = hvild.insert(Post)
- update = hvild.update(Post)
- @f3.auto_method(returns=PostMessage, name="get_by_title")
- def get_by_title(self, request, title=(str,)):
- query = Post.query(Post.title==title)
- post = query.get()
- if not post:
- raise f3.NotFoundException()
- if not post.key.kind() == 'Post':
- raise f3.InvalidRequestException()
- message = f3.messages.serialize(PostMessage, post)
- return message
- Where to go from here
- You've created your API backend so now you probably want to create some sort of front-end to talk to it. Most commonly you'll be writing a JavaScript client so head over to Google's documentation on Javascript API Clients. There's also guides for Android and iOS!
- To dig deeper into what Ferris has to offer check out the :doc:`users_guide/index`.
- Mini_django.py An entire django app in a single file. Updated from here to use Django trunk.
- pico
- This started off to see what the absolutely smallest requirements needed to run a Django project. Run the pico_django.py with $ PYTHONPATH=. django-admin.py runserver 0.0.0.0:8000 --settings=pico_django and go to http://localhost:8000
- Or with uwsgi in production:
- $ uwsgi --http :8000 -M --pythonpath=. \
- --env DJANGO_SETTINGS_MODULE=pico_django \
- -w "django.core.handlers.wsgi:WSGIHandler()"
- pico_django.py
- from django.http import HttpResponse
- from django.conf.urls import url
- DEBUG = True
- ROOT_URLCONF = 'pico_django'
- DATABASES = {'default': {}}
- def index(request, name):
- return HttpResponse('Hello {name}!'.format(name=(name or 'World')))
- urlpatterns = (
- url(r'^(?P<name>\w+)?$', index)
- )
- SECRET_KEY = "not so secret"
- mini
- Soon pico needed a little more spice, so it got some template loading and then because I'm lazy I made the new version directly runnable.
- Run the mini_django.py with $ python ./micro_django.py and go to http://localhost:8000/Foo
- mini_djanog.py
- '''
- Run this with `$ python ./miny_django.py runserver` and go to http://localhost:8000/
- '''
- import os
- import sys
- from django.conf import settings
- from django.conf.urls import patterns
- from django.http import HttpResponse
- # this module
- me = os.path.splitext(os.path.split(__file__)[1])[0]
- # helper function to locate this dir
- here = lambda x: os.path.join(os.path.abspath(os.path.dirname(__file__)), x)
- # SETTINGS
- DEBUG = True
- ROOT_URLCONF = me
- DATABASES = {'default': {}} # required regardless of actual usage
- TEMPLATE_DIRS = (here('.'), )
- SECRET_KEY = 'so so secret'
- if not settings.configured:
- settings.configure(**locals())
- # Settings must be configured before importing
- from django.views.decorators.csrf import csrf_exempt
- # VIEW
- @csrf_exempt
- def index(request):
- return HttpResponse("Hello from mini_django.py")
- # URLS
- urlpatterns = patterns('', (r'^$', index))
- if __name__ == '__main__':
- # set the ENV
- sys.path += (here('.'),)
- # run the development server
- from django.core import management
- management.execute_from_command_line()
- Dependencies
- python
- django
- Install
- Install django
- get the code wget https://github.com/readevalprint/mini-django/raw/master/mini_django.py
- run it $ python ./mini_django.py
- open browser http://localhost.com/Foo
- License
- As-is. Public Domain. Don't blame me.
- Author
- Tim Watts (tim@readevalprint.com)
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement