home :: django

Tue, 10 Apr 2012

The state of Django money.

Updated (2012-04-11): Now contains the final and correct pip install command.

TL;DR: pip install django-money will give you a working set of Django fields that handle Money and Currency. Read below for how that happened.

I began this Easter weekend trying to finish up one of my pet-projects and get it ready to publish. The goal of the website is to compare providers, of the same thing, from around the world.

Representing prices is simple. A float will do if you are in a hurry.

Except.

When you need to perform calculations and you run into rounding errors. At that point you will likely discover the Decimal module.

Which also works perfectly fine. And if you only ever deal in a single market that is fine.

But across markets? That requires Money. Money is normally defined as a value and currency. Once you have both of those, you can then do cross-market comparison – and upgrade you program to handle multi-currency – easily.

I hoped that I was not the only person who had realised this, so I started to look around.

And I struck gold! I found Python Money. It even included appropriate Django fields as well.

Unfortunately it had had no updates since 2008. ☹. Worse it didn't work so well. I continued my search.

I kept on looking and found Django Money. Even better. This separated out the Django support and the Python support.

It turned out Jakewin's py-moneyed was actually a fork of Limist's py-moneyed.

So I set about retrieving every fork and comparing them.

It turns out that Jakewin's version was good, just out-of-date. It mentioned waiting until a pull request has been merged in to limist's version.

Which had been done a while ago. And Jakewin's Django-money (djmoney) was nicely done too, unittests and all. It had just bitrotted compared to newer versions of Django (and South).

I discovered there were a set of forks of django-money at github too. I analysed them all, and basically only jakewins's and reinbach's mattered.

I created fixes for jakewins and reinbach's version.

I also opened up on an issue asking limist's to tag and upload a new version of py-moneyed.

I also got a response from limist and now you can do pip install py-moneyed and get a useful, working, version.

And reinbach also responded and now you can do pip install django-money and get a working, usable, representation of Money within Django.

It turned out to be a productive, if yak-shaving, weekend after all.

[ / django] Trackbacks (0) Comments (0) permanent link permanent link

Wed, 31 Aug 2011

PSA: Use a 303 (or 307).

HTTP has redirect codes. Lots of them. The full list of codes is available on wikipedia.

But some common ones are widely used and widely misunderstood.

TL;DR: After you receive a POST, and you want the client to perform a GET, return a 303.

Here are some of the redirection codes:

301
Moved Permanently
302
Found (originally Moved Temporarily)
303
See Other
307
Temporary Redirect

Basically, if you create something server side and you make that thing available via GET. Send back a 303.

If, when a POST is received you would like it done somewhere else (e.g. from one cluster to another) use a 307.

The full and complete historial gory details are outlined by Eric Law'sIE Internals blog.

This post inspired by the redirect misuse on (an otherwise informative) blog post by Valya Golev's.

[ / django] Trackbacks (0) Comments (0) permanent link permanent link

Wed, 04 May 2011

Django and Whiskey.

No, not programming whilst inebriated. Although the effects of the Ballmer Peak are well known.

I mean using wsgi and Apache together with Django.

But, perhaps, I should explain my (current) layout of projects.

I now use:

 /path/to/project/
                 .git
                 .gitignore
                 project_name/
                             urls.py
                             settings.py
                             production_settings.py
                             [...]
                 apache/
                       project_name.conf
                       django.wsgi
                 env/
                    [...]
                 sitestatic/
                 requirements.txt
                 fabfile.py

This allows me to have revision control of things meta to the project and of the project itself.

This setup is relatively new, I've only been using it in about 4 projects now.

Which meant that my wsgi file had to be slightly modified.

This took me a lot longer than I expected to get going. But if you decide to use a layout similar to mine, this might help.

import os
import sys
import site

## Assumptions:
# Assume that the Django project and the environment are at the same level.
# i.e.
# /home/
#      /<project root>/                                     <- *
#                     /<project name>/                      <- *
#                                    /apache/
#                                           django.wsgi
#                                    production_settings
#                     /env/
#                         /lib/
#                             /pythonX.Y/
#                                       /site-packages/     <- *
#                     /sitestatic
#
# Directories marked with '<- *' need to be imported for everything to work

PROJECT_NAME = "control"

PYVER = "%d.%d" % (sys.version_info[0], sys.version_info[1])
APACHE_DIR = os.path.abspath(os.path.dirname(__file__))
PROJECT_ROOT = os.path.abspath(os.path.join(APACHE_DIR, ".."))

site_packages = os.path.join(PROJECT_ROOT, 'env/lib/python%s/site-packages' % PYVER)
site.addsitedir(os.path.abspath(site_packages))
sys.path.insert(0, os.path.join(PROJECT_ROOT, PROJECT_NAME))
sys.path.insert(0, PROJECT_ROOT)

# import from down here to pull in possible virtualenv django install
os.environ['DJANGO_SETTINGS_MODULE'] = 'production_settings'

import django.core.handlers.wsgi
application = django.core.handlers.wsgi.WSGIHandler()

One slight tweak I have also done is to rename the django.wsgi file, to be the environment file. e.g. devel.wsgi.

That means the only hardcoded thing in the wsgi file is the PROJECT_NAME. Apply this patch on top of the file above:

--- apache/django.wsgi  2011-05-06 00:38:57.266316530 +0100
+++ apache/devel.wsgi   2011-05-05 14:23:33.708685680 +0100
@@ -24,14 +24,15 @@
 PYVER = "%d.%d" % (sys.version_info[0], sys.version_info[1])
 APACHE_DIR = os.path.abspath(os.path.dirname(__file__))
 PROJECT_ROOT = os.path.abspath(os.path.join(APACHE_DIR, ".."))
+ENVIRON_TYPE = os.path.basename(__file__).rstrip('.wsgi')

-site_packages = os.path.join(os.path.dirname(PROJECT_ROOT), 'env/lib/python%s/site-packages' % PYVER)
+site_packages = os.path.join(PROJECT_ROOT, 'env/lib/python%s/site-packages' % PYVER)
 site.addsitedir(os.path.abspath(site_packages))
 sys.path.insert(0, os.path.join(PROJECT_ROOT, PROJECT_NAME))
 sys.path.insert(0, PROJECT_ROOT)

 # import from down here to pull in possible virtualenv django install
-os.environ['DJANGO_SETTINGS_MODULE'] = 'production_settings'
+os.environ['DJANGO_SETTINGS_MODULE'] = '%s_settings' % ENVIRON_TYPE

 import django.core.handlers.wsgi
 application = django.core.handlers.wsgi.WSGIHandler()

If you have any suggestions for improvements, or thoughts on how to remove the PROJECT_NAME, let me know!

[ / django] Trackbacks (0) Comments (0) permanent link permanent link

Fri, 25 Jun 2010

Beginning Django Development

Oddly, I have had four different people ask me about the best way to begin a Django project or setup their system to make it easy to develop with Django.

Since I've now given my response via email so many times, I figure I might as well broadcast it and hopefully this will help others as well

My assumptions:

What we will do is create a private binary directory and another Python modules. Then take a checkout of the two most important Django projects, and make them work.

First: create your user

$ sudo adduser newbie
$ ssh localhost -l newbie
$ whoami
newbie

Then we want to create some local bin and lib directories. First the binary directory

$ mkdir ~/bin # On Ubuntu/Debian, this will automatically be in your PATH the next time you login

And now a local directory for our various libraries

$ mkdir -p ~/lib/python
$ echo "PYTHONPATH=~/lib/python:" >> ~/.bashrc
$ echo "export PYTHONPATH" >> ~/.bashrc

This will add that directory to our Python path. If you happen to also use another language you can put things into ~/lib/ruby, ~/lib/perl as appropriate

$ mkdir ~/Projects
$ cd ~/Projects

Here is where we will store copies of upstream software. What is the reason for using the repositories rather than packages? This allows us to checkout specific versions to match what our clients might be using. Or test things against newer versions of the upstream project.

First, let's setup Django. I tend to use git by default, especially if the upstream is using Subversion. If they are using Mercurial or Bazaar, I use those directly.

$ git svn clone -s http://code.djangoproject.com/svn/django/
Initialized empty Git repository in /home/newbie/Projects/django/.git/
Using higher level of URL: http://code.djangoproject.com/svn/django => http://code.djangoproject.com/svn
r1 = 5cda37203ffa6ea83da2958a95c377984482877f (refs/remotes/trunk)
	A	django-docs/images/flatfiles_admin.png
	A	django-docs/images/users_changelist.png
	A	django-docs/model-api.txt
	A	django-docs/build.py
	A	django-docs/db-api.txt
	A	django-docs/writing-apps-guide-outline.txt
	A	django-docs/templates.txt
r2 = b8249ac45e2154933b9649fd8181d5769e31c9fc (refs/remotes/trunk)
	A	django/utils/feedgenerator.py
	A	django/utils/datastructures.py
[...]
r13399 = f3902c67a3b8788de2145899e435a394c512b455 (refs/remotes/releases)
	M	tests/regressiontests/m2m_through_regress/tests.py
r13400 = cd72207306a5a4eecdf07f65c109f37c8317ed81 (refs/remotes/trunk)
$

Now we have Django, push it into our python path

$ cd ~/lib/python
$ ln -s ~/Projects/django/django/
$ python
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import django
>>> django.VERSION
(1, 2, 1, 'final', 0)

Now, let's do the same for South. South is a database migration helper for Django. These are the two things you want to have in any Django at a minimum. There are also plenty of other amazing things like haystack, piston, satchmo, etc. You can follow the same recipie for them too.

$ cd ~/Projects
$ hg clone http://bitbucket.org/andrewgodwin/south/
destination directory: south
requesting all changes
adding changesets
adding manifests
adding file changes
added 802 changesets with 1340 changes to 183 files (+1 heads)
updating to branch default
143 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ cd ~/lib/python
$ ln -s ~/Projects/south/south
$ python 
Python 2.6.5 (r265:79063, Apr 16 2010, 13:57:41) 
[GCC 4.4.3] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import south
>>> south.__version__
'0.7.1'

Finally, we want to be able to create Django projects. There is a great helper called django-admin that will do this. Let's put it into our path

$ cd ~/bin
$ ln -s ~/Projects/django/django/bin/django-admin.py
$ hash -r

That last statment will cause your interpreter to re-read PATH and make anything new available for execution.

Now, you should go ahead and create your projects. There are a variety of ways to do this. However what I like to do is keep Django applications separate from the Django project. That way, they can easily be re-used if required.

So, for a project called 'foo', we might have

$ mkdir -p ~/Work/foo
$ cd ~/Work/foo
$ mkdir foo.example.com
$ mkdir templates
$ mkdir media
$ mkdir <individual apps>

For each individual app, put them into the Python library. And you then have your re-usability from within Django. For a application called 'bar', you would do:

$ cd ~/Work/foo
$ django-admin.py startapp bar
$ cd ~/lib/python
$ ln -s ~/Work/foo/bar

And now, in your Django foo.example.com setting.py's file you can put 'bar' as one the installed applications and things will Just Work

Obviously there are many way to slice this particular mango, but I've found that this works pretty well for me. You can spruce it up by revision controlling each directory in your project (I do) and also take advantage of things like virtualenv and Fabric to make deploying just as easy as developing. But I'll leave those topics until a later date

[ / django] Trackbacks (0) Comments (0) permanent link permanent link

About

ॐ (aum) - what was, what is and what will be, wildfire's musings

Anand Kumria
wildfire@progsoc.org

Calendar

Topics

Subscribe

Subscribe to a syndicated feed of my weblog, brought to you by the wonders of Atom.

Music

 

Blosxom

Rendered in only 0.7978 seconds.

Powered by blosxom

Web Standards

Valid XHTML 1.1! Valid CSS! Uses microformats!