Skip to content

Commit

Permalink
django design patterns book completed - added agiliq theme
Browse files Browse the repository at this point in the history
  • Loading branch information
saikiran committed Oct 24, 2011
1 parent 54e1df3 commit 1db94b3
Show file tree
Hide file tree
Showing 64 changed files with 6,322 additions and 13 deletions.
Binary file added build/doctrees/contents.doctree
Binary file not shown.
Binary file added build/doctrees/environment.pickle
Binary file not shown.
Binary file added build/doctrees/forms.doctree
Binary file not shown.
Binary file added build/doctrees/index.doctree
Binary file not shown.
Binary file added build/doctrees/misc.doctree
Binary file not shown.
Binary file added build/doctrees/models.doctree
Binary file not shown.
Binary file added build/doctrees/templates.doctree
Binary file not shown.
Binary file added build/doctrees/urls.doctree
Binary file not shown.
Binary file added build/doctrees/views.doctree
Binary file not shown.
Binary file added build/doctrees/workflow.doctree
Binary file not shown.
4 changes: 4 additions & 0 deletions build/html/.buildinfo
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file hashes the configuration used when building these files. When it is not found, a full rebuild will be done.
config: 3ca1cc8b4da783defdc657615ddaeb59
tags: fbb0d17656682115ca4d033fb2f83ba1
29 changes: 29 additions & 0 deletions build/html/_sources/contents.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
==================================================
Welcome to Django design patterns's documentation!
==================================================

Prerequisites
------------------

1. You know Python. If you do not, try `Dive into Python <http://diveintopython.org/>`_
2. You know Django. You work with Django 1.0+. If you do not try `Djangobook <http://www.djangobook.com/>`_

Getting the docs
----------------------
The documents are generated using `Sphinx <http://sphinx.pocoo.org/>`_. The code
is available on `Github <http://github.com/agiliq/django-design-patterns/tree/master>`_.
The generated Html is available on http://uswaretech.com/books/djangodesignpatterns/

To generate the docs, do `make html` within the top level folder of repo.

License
-------------
This work is dual licensed under
`Creative Commons Attribution-Share Alike 3.0 Unported License <http://creativecommons.org/licenses/by-sa/3.0/>`_
and
`GNU Free Documentation License <http://en.wikipedia.org/wiki/Wikipedia:Text_of_the_GNU_Free_Documentation_License>`_





140 changes: 140 additions & 0 deletions build/html/_sources/forms.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
=================
Forms
=================

Prefer ModelForm to Form
--------------------------
ModelForm already know the correct UI widgets for your underlying Models. In
most of the cases ModelForm would suffice instead of Forms.

Some common scenarios

Hiding some fields from ModelForm which are needed for a DB save.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Eg, you want to create a profile for the logged in user.::

#in Forms.py
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ['user',]

#In Views:
form = ProfileForm(request.POST)
profile = form.save(commit = False)
profile.user = request.user
profile.save()

Or::

#Todo test this
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude =['user',]
def __init__(self, user, *args, **kwargs)
self.user = user
super(ProfileForm, self).__init__(*args, **kwargs)

def save(*args, **kwargs):
self.instance.user = self.user
super(ProfileForm, self).save(*args, **kwargs)


Customizing widgets in ModelForm fields
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Sometimes you just need to override the widget of a field that's already on
your ModelForm. Instead of duplicating the field definition (with `help_text`,
`required`, `max_length`, etc). You can do this::

from django.contrib.admin.widgets import AdminFileWidget

class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = 'user',

def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
# note that self.fields is available just after calling super's __init__
self.fields['picture'].widget = AdminFileWidget()


Saving multiple Objects in one form
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

As::

class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ['user',]

class UserForm(forms.ModelForm):
class Meta:
model = User
exclude = [...]

#in views.py
userform = UserForm(request.POST)
profileform = ProfileForm(request.POST)
if userform.is_valid() and profileform.is_valid():
#Only if both are valid together
user = userform.save()
profile = profileform.save(commit = False)
profile.user = user
profile.save()

{# In templates #}
<form ...>
{{ userform }}
{{ profileform }}
<input type="submit" />
</form>




Forms should know how to save themselves.
---------------------------------------------
if your forms is a forms.ModelForm, it already knows how to save its data. If you
write a forms.Form, it should have a `.save()`. This keeps things symmetrical with
`ModelForms`, and allows you to do::

#in views.py
def view_func(request):
if request.method == 'POST':
form = FormClass(request.POST)
if form.is_valid():
obj = form.save()
...
...

Instead of::

if form.is_valid():
#handle the saving in DB inside of views.

The `.save()` should return a Model Object


The form should know what to do with it's data
------------------------------------------------

If you're building a contact form, or something like this, the goal of your form is
to send an email. So this logic should stay in the form::

class ContactForm(forms.Form):
subject = forms.CharField(...)
message = forms.TextField(...)
email = forms.EmailField(...)
...

def save(self):
mail_admins(self.cleaned_data['subject'], self.cleaned_data['message'])

I've used `save()`, and not `send()`, even when i'm not really saving anything.
This is just a convention, people prefer to use `save()` to keep the same interface to
ModelForms. But it doesn't really matter, call it whatever you want.
43 changes: 43 additions & 0 deletions build/html/_sources/index.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
.. Django design patterns documentation master file, created by sphinx-quickstart on Thu Jun 18 21:08:53 2009.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.

=================
Index
=================

This is a collection of patterns which we have found occuring commonly with
Django. All of these either make collaboration easier, coding simpler or code
more maintainable. None of them are design patterns in the sense of GoF design
patterns. We call them design patterns as none other seem closer or more
convinient.

These are guidelines, which need to be overriden (very commonly, in some cases).
Use your judgement when using them. As PEP8 says, "Foolish consistency is the
hobgoblin of small minds."

Contents:

Chapters
===========

.. toctree::
:maxdepth: 2



contents
urls
models
views
forms
templates
workflow
misc

Indices and tables
==================

* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`
135 changes: 135 additions & 0 deletions build/html/_sources/misc.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
=================
Misc
=================

settings.py and localsettings.py
------------------------------------
The settings for your project which are a machine specific should be refactored
out of settings.py into localsettings.py. In your settings.py, you should do::

try:
from localsettings import *
except ImportError:
print 'localsettings could not be imported'
pass #Or raise

This should be at the end of settings.py, so that localsetting.py override
settings in settings.py

This file should not be checked in your repository.

Use relative path in settings.py
--------------------------------------
Instead of writing::

TEMPLATE_DIRS = '/home/user/project/templates'

Do::

#settings.py
import os

CURRENT_DIR = os.path.dirname(__file__)
TEMPLATE_DIRS = os.path.join(CURRENT_DIR, 'template')

Apps should provide default values for settings they are trying to read.
---------------------------------------------------------------------------
As far as possible, apps should have defaults for settings they are trying to
read. Instead of::

DEFAULT_SORT_UP = settings.DEFAULT_SORT_UP

Use::

DEFAULT_SORT_UP = getattr(settings, 'DEFAULT_SORT_UP' , '&uarr;')



Use templatetags when the output does not depend on the request
-------------------------------------------------------------------
In the sidebar, you want to show the 5 latest comments. You do not need
the request to output this. Make it a templatetag.

Import as if your apps are on your project path
----------------------------------------------------
Instead of doing `from project.app.models import ModelClass` do `from app.models
import ModelClass`. This makes you apps reusable as they are not tied to a project.

Naming things
-----------------

Model class names should be singular, not plural.::

class Post(models.Model):
...

and not::

class Posts(models.Model):
...

Foreign key should use the name of the referenced class.::

class Post(models.Model):
user = models.ForeignKey(User)

Querysets should be plural, instances should be singular.::

posts = Post.objects.all()
posts = Post.objects.filter(...)

post = Post.object.get(pk = 5)
post = Post.object.latest()

Using pdb remotely
------------------------
Sometimes you will hit bugs which show up on server but not on your local
system. To handle these, you need to debug on the server. Doing `manage.py
runserver` only allows local connections. To allow remote connections, use::

python manage.py runserver 0.0.0.0:8000

or::

python manage.py runserver 0:8000


So that your `pdb.set_trace()` which are on remote servers are hit when you access
them from your local system.

Do not use primary keys in urls
-----------------------------------
If you use PK in urls you are giving away sensitive information, for example,
the number of entries in your table. It also makes it trivial to guess other urls.

Use slugs in urls. This has the advantage of being both user and SEO
friendly.

If slugs do not make sense, instead use a CRC algorithm.::

class Customer(models.Model):
name = models.CharField(max_length = 100)

def get_absolute_url(self):
import zlib
#Use permalink in real case
return '/customer/%s/' % zlib.crc32(self.pk)


Code defensively in middleware and context processors.
-----------------------------------------------------------
Your middleware and context processors are going to be run for **all** requests.
Have you handled all cases?

def process_request(request):
if user.is_authenticated():
profile = request.user.get_profile()#Hah, I create profiles during
#registration so this is safe.
...

Or it is? What about users created via `manage.py createsuperuser`? With the
above middleware, the default user can not access even the admin site.

Hence handle all scenarios in middleware and context processors. This is one place
where `try: .. except: ..` (bare except) blocks are acceptable. You do not want one
middleware bringing down the entire site.

0 comments on commit 1db94b3

Please sign in to comment.