Tue, 10 Apr 2012
Updated (2012-04-11): Now contains the final and correct pip install command.
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.
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.
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 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.
Wed, 31 Aug 2011
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:
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.
Wed, 04 May 2011
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, sys.version_info) 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.
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, sys.version_info) 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!
Fri, 25 Jun 2010
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
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
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.
$ 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
ॐ (aum) - what was, what is and what will be, wildfire's musings
Subscribe to a syndicated feed of my weblog, brought to you by the wonders of Atom.
Rendered in only 0.7978 seconds.