How do I retroactively make AutoFields that don't break django-admin? - django

I created the models in a Django app using manage.py inspectdb on an
existing postgres database. This seemed to work except that all the
primary keys were described in the models as IntegerFields, which made
them editable in the admin panel, and had to be hand-entered based on
knowledge of the id of the previous record. I just learned about this
after some usage by the client, so I went back to change things like
blog_id = models.IntegerField(primary_key=True)
...to...
blog_id = models.AutoField(primary_key=True)
Now the id fields don't appear in the admin panel (good), but adding new rows
has become impossible (not good).
IntegrityError at /admin/franklins_app/blog/add/
duplicate key value violates unique constraint "blog_pkey"
What's the fix? (Bonus question: is it possible to capture the value that Django is trying to assign as the primary key for the new row?)

The sequence behind the serial field which is your primary key doesn't know about the manually entered records.
Find the maximum value of the primary key:
SELECT MAX(<primary_key>) FROM <your_table>;
Then set the next value of the underlying sequence to a number greater than that:
SELECT SETVAL('<primary_key_seq>', <max_value_in_primary_key_plus_something_for_safety>);
You'll find the name of the sequence (mentioned above as <primary_key_seq>) using:
SELECT pg_get_serial_sequence('<your_table_name>', '<primary_key_column_name');

Related

Change primary key on manytomany field Django

Is it possible to change primary key of many to many field from default to uuid?
Table is already populated. What is the best way for migration?
You can create a migration that executes raw queries, add a new field to the table in the middle then generate the new UUID.
After that, another set of queries to drop the constraints on ID, add the new constraints to the new UUID field, and lastly drop the old ID field.
https://docs.djangoproject.com/en/2.0/ref/migration-operations/#runsql

Django Postgres DateRangeField - can I use UNIQUE vaildator

I'm trying to use some of the shiny new Postgres fields in Django.
For a booking system the DateRangeField seems perfect.
Can I use the validators UNIQUE or even UNIQUE_DATE in regard to this field?
The excellent Django documentation is, unfortunately, not elaborating regarding validators and the Postgres fields.
So basically, to my knowledge, the field option unique and unique_for_date do not work with Postgres DateRange fields.
However, there is a way using a Porstgres index
We need a btree_gist combined index in Postgres.
This is a Postgres extension and it is provided with Postgres, we just need to install it into the database with
CREATE EXTENSION btree_gist;
Once we have this, we can have a constraint on the date_range field with combined with another field in our table.
Say we have a hotel room number (room_id) and a booking date range (dt_range), which, combined, should be unique. Our constraint would be
ALTER TABLE <yourtablename> ADD EXCLUDE USING gist ( room_id WITH =, dt_range WITH && );
Now, whenever we have a booking for room room_id with conflicting booking dates, Postgres will throw an error which is a
django.db.utils.IntegrityError
I hope that helps!

What happens when a Django table hits max on an ID/PK column?

Lets imagine that you have an application where an specific table has A LOT new objects (in Django terms) created. Nevermind how many are deleted, but let's say it is sufficient to keep the table functional.
After quite a while, how unlikely it may ever be, the table hits max on the ID/PK column. What happens next? How does Django handle that situation?
I tried to set an id of 2147483648 with a Postgres database. Django raises DataError: integer out of range. If I try the same in Postgres I get [22003] ERROR: integer out of range. So it looks like Django just throws the db error.
If I search the Django source code for DataError I find:
https://github.com/django/django/blob/3c447b108ac70757001171f7a4791f493880bf5b/django/db/utils.py#L53 which confirms that Django re-throws backend-specific database exceptions.
You can always define the primary key field yourself and mark it as primary:
id = models.BigIntegerField(primary_key=True)
This will increase id's form 2147483647 to 9223372036854775807.
If that isn't enough switch to uuid:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
https://docs.djangoproject.com/en/1.10/topics/db/models/#automatic-primary-key-fields and https://docs.djangoproject.com/en/1.10/ref/models/fields/#bigintegerfield and https://docs.djangoproject.com/en/1.10/ref/models/fields/#uuidfield

how django knows to update or insert

I'm reading django doc and see how django knows to do the update or insert method when calling save(). The doc says:
If the object’s primary key attribute is set to a value that evaluates to True (i.e. a value other than None or the empty string), Django executes an UPDATE.
If the object’s primary key attribute is not set or if the UPDATE didn’t update anything, Django executes an INSERT link.
But in practice, when I create a new instance of a Model and set its "id" property to a value that already exist in my database records. For example: I have a Model class named "User" and have a propery named "name".Just like below:
class User(model.Model):
name=model.CharField(max_length=100)
Then I create a new User and save it:
user = User(name="xxx")
user.save()
now in my database table, a record like id=1, name="xxx" exists.
Then I create a new User and just set the propery id=1:
newuser = User(id=1)
newuser.save()
not like the doc says.when I had this down.I checked out two records in my database table.One is id = 1 ,another is id=2
So, can anyone explain this to me? I'm confused.Thanks!
Because in newer version of django ( 1.5 > ), django does not check whether the id is in the database or not. So this could depend on the database. If the database report that this is duplicate, then it will update and if the database does not report it then it will insert. Check the doc -
In Django 1.5 and earlier, Django did a SELECT when the primary key
attribute was set. If the SELECT found a row, then Django did an
UPDATE, otherwise it did an INSERT. The old algorithm results in one
more query in the UPDATE case. There are some rare cases where the
database doesn’t report that a row was updated even if the database
contains a row for the object’s primary key value. An example is the
PostgreSQL ON UPDATE trigger which returns NULL. In such cases it is
possible to revert to the old algorithm by setting the select_on_save
option to True.
https://docs.djangoproject.com/en/1.8/ref/models/instances/#how-django-knows-to-update-vs-insert
But if you want this behavior, set select_on_save option to True.
You might wanna try force_update if that is required -
https://docs.djangoproject.com/en/1.8/ref/models/instances/#forcing-an-insert-or-update

Django AutoField not returning new primary_key

We've got a small problem with a Django project we're working on and our postgresql database.
The project we're working on is a site/db conversion from a PHP site to a django site. So we used inspect db to generate the models from the current PHP backend.
It gave us this and we added the primary_key and unique equals True:
class Company(models.Model):
companyid = models.IntegerField(primary_key=True,unique=True)
...
...
That didn't seem to be working when we finally got to saving a new Company entry. It would return a not-null constraint error, so we migrated to an AutoField like below:
class Company(models.Model):
companyid = models.AutoField(primary_key=True)
...
...
This saves the Company entry fine but the problem is when we do
result = form.save()
We can't do
result.pk or result.companyid
to get the newly given Primary Key in the database (yet we can see that it has been given a proper companyid in the database.
We are at a loss for what is happening. Any ideas or answers would be greatly appreciated, thanks!
I just ran into the same thing, but during a django upgrade of a project with a lot of history. What a pain...
Anyway, the problem seems to result from the way django's postgresql backend gets the primary key for a newly created object: it uses pg_get_serial_sequence to resolve the sequence for a table's primary key. In my case, the id column wasn't created with a serial type, but rather with an integer, which means that my sequence isn't properly connected to the table.column.
The following is based on a table with the create statement, you'll have to adjust your table names, columns and sequence names according to your situation:
CREATE TABLE "mike_test" (
"id" integer NOT NULL PRIMARY KEY,
"somefield" varchar(30) NOT NULL UNIQUE
);
The solution if you're using postgresql 8.3 or later is pretty easy:
ALTER SEQUENCE mike_test_id_seq OWNED BY mike_test.id;
If you're using 8.1 though, things are a little muckier. I recreated my column with the following (simplest) case:
ALTER TABLE mike_test ADD COLUMN temp_id serial NOT NULL;
UPDATE mike_test SET temp_id = id;
ALTER TABLE mike_test DROP COLUMN id;
ALTER TABLE mike_test ADD COLUMN id serial NOT NULL PRIMARY KEY;
UPDATE mike_test SET id = temp_id;
ALTER TABLE mike_test DROP COLUMN temp_id;
SELECT setval('mike_test_id_seq', (SELECT MAX(id) FROM mike_test));
If your column is involved in any other constraints, you'll have even more fun with it.