You have an old database and you find the following happens.
>>> ap = ValidAirPressureCode.objects.get(code='9')
In short Django doesn’t return the id of a newly inserted record. If you insert records in admin and choose to continue editing, you’ll get an error saying it can’t find a record with a null id. The problem (in my case) is that Django uses a postgres function called “pg_get_serial_sequence” to identify where the last inserted number given to a table’s record is stored but if your tables were not created using the serial type, this will return null. In my case it was because the database was nine years old and serial types did not exist then.
A proposed solution can be seen on this ticket but at the time of writing it is not implemented.
To fix it in my case I took a look at what my sequences were called. For example:
id | integer| \ not null default \ nextval(('t_valid_airpressure_pk_seq'::text)::regclass)
. I then wrote the following piece of middleware (just inserted a call to it in my settings.py middleware section).
def my_last_insert_id(self, cursor, table_name, pk_name):
""" This code fails on the alc database, so we fall back to a method of
getting the last id that does work"""
sql = "SELECT CURRVAL(pg_get_serial_sequence('%s','%s'))" % (self.quote_name(table_name), pk_name)
result = cursor.fetchone()
if not result:
sql = "select currval('%s_pk_seq'::regclass)" % table_name
result = cursor.fetchone()
from django.db import connection
connection.ops.__class__.last_insert_id = my_last_insert_id
# Tell Django to forget about this middleware now, we have
# had our evil way.
from django.core.exceptions import MiddlewareNotUsed
Basically I monkey patch the last_insert_id method. I call the normal code and if that fails try with my own way. All my sequences have the same naming convention (tablename_pk_seq) so it works for me.
Thanks to Ross for pointing me in the right direction, although he didn’t approve of my monkey patching solution 😉
I have done this quite a few times in the past, but not recently. I was given three days to convert an existing postgres database (front ended with Zope) to Django 1.3. I thought it would be useful to document what I did here for my own future reference and to record any gotchas for posterity. The database I am converting is nine years old, so plenty of cruft through the years, although the basic structure is sound.
Note that the website of this application is not particularly complicated. Most of the core work of this application is done via backend processes written with Twisted. The front end is used for displaying the system status and allowing for data to be modified, with a few additional complications. The system will work without any web front end (although this is hardly ideal) without stopping production, so the risk is minimised.
This is the first day – although I only started from the beginning of the afternoon, so it’s the first half a day.
I installed PostgreSQL on my new MacBook Air today via Homebrew. Having installed it I was mystified why I could not connect without specifically specifying the host was localhost. e.g.
yes:postgres ian$ psql --list psql: could not connect to server: Permission denied Is the server running locally and accepting connections on Unix domain socket \ "/var/pgsql_socket/.s.PGSQL.5432"?
Instead I would have to specify “psql –host=localhost –list”.
Turns out that 10.7 has postgres installed by default (or at least bits of it), but not configured to run. The postgres commands that you type are picked up by the default install as they are first on the path (they are in /usr/bin/, whereas the Homebrew binaries appear in /usr/local/bin/). This posting has more info.
I have changed my previous solution (moving the postgres files out of /usr/bin), which was stupid (and will give similar issues with other Homebrew installs. The correct solution is to amend ~/.bash_profile (create if it doesn’t exist) and add the following line:
Start a new terminal and you should be good to go.