I'm switching from a MySQL backend to a PostgreSQL backend and am running into some model inheritance issues. Here is an example of the models:
class Parent(models.Model):
key = models.Charfield(...)
value = models.Charfield(...)
content_type = models.ForeignKey(ContentType)
object_id = models.CharField(max_length=200)
content_object = generic.GenericForeignKey('content_type', 'object_id')
class Child1(Parent):
pass
class Child2(Parent):
pass
The reason we have two child classes like this is, we're simulating two key/value pairs in another model, and wanted to separated them into two tables for an easier lookup. The Generic FKs were also for attaching this to other models. This inheritance setup works fine in MySQL, but when I switched it to PostgreSQL, I get an error when trying to run our tests (but syncdb works fine). It's as if Django is OK with the relationship but PostgreSQL doesn't like the SQL being generated. When I look at what's being generated from syncdb I see this:
CREATE TABLE "myapp_parent" (
"id" serial NOT NULL PRIMARY KEY,
"key" varchar(200) NOT NULL,
"value" varchar(200) NOT NULL,
"content_type_id" integer NOT NULL REFERENCES "django_content_type" ("id") DEFERRABLE INITIALLY DEFERRED,
"object_id" varchar(200) NOT NULL);
CREATE TABLE "myapp_child1" (
"parent_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_parent" ("id") DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE "myapp_child2" (
"parent_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_parent" ("id") DEFERRABLE INITIALLY DEFERRED);
So everything looks right, then when I run my tests I get this:
Error: Database test_myapp couldn't be flushed. Possible reasons:
* The database isn't running or isn't configured correctly.
* At least one of the expected database tables doesn't exist.
* The SQL was invalid.
Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.
The full error: column "id" of relation "myapp_child1" does not exist
When I run flush:
SELECT setval(pg_get_serial_sequence('"myapp_child1"','id'), 1, false);
I've tried manually adding an ID field as the primary key in the child model but Django throws an error saying it conflicts with the Parent's ID field. How do I fix this so PostgreSQL likes it? And thanks in advance.
If you're using model inheritance in django, you should declare class Parent to be abstract
class Parent(models.Model):
...
class Meta:
abstract = True
See the docs. I imagine that some postgres / mysql differences have only been tested against standard conforming code - which could be why you're having problems here. I'd also recommend ./manage.py syncdb after making these changes ;-)
If in doubt, and on in a testing environment, you can drop your tables and start again with
$ ./manage.py sqlclear | ./manage.py dbshell
Your model must contain one - and only one - foreign key to the target model. If you have more than one foreign key, a validation error will be raised. This is one of the restrictions of django.
Related
I have an unmanaged model in Django:
class Person(models.Person):
name = models.CharField(max_length=200)
class Meta:
managed = False
db_table = '"public"."person"'
Somewhere in my tests, I try to create a Person entry in the DB:
person = Person(name="Ariel")
person.save()
But then I get an error:
django.db.utils.IntegrityError: null value in column "id" of relation "person" violates not-null constraint
DETAIL: Failing row contains (null, Ariel).
Outside tests, everything works fine. In the tests, I initialize the DB with the tables referenced by the unmanaged by loading a schema dump.
The Django docs states that "no database table creation, modification, or deletion operations will be performed for this model", and that "all other aspects of model handling are exactly the same as normal", including "adding an automatic primary key field to the model if you don’t declare it". But doesn't that mean this code should work? How come Django and Postgres are not taking care of the id? Am I doing something wrong? How can I fix it?
Surely the issue that you're having is that in your PostgreSQL table, id is not an auto incremental field and it nulls by default.
It's quite a common issue when using unmanaged models on Django. You need to cover every single aspect of the table you're using.
ProgrammingError at /app-name/url-name
relation "TableName" does not exist
LINE 1: SELECT (1) AS "a" FROM "TableName" WHERE "TableName..."
I have done an inspectdb, confirmed the mapping matches what is automatically set when running makemigrations, and deleted all migrations from the django_migrations table and re-ran migrate. I also checked that the migrations are successfully imported, by looking at the django_migrations table,as well as running showmigrations.
I wonder why I see the double quotes around the table names, is that why new models cannot be used? Interestingly, if I reference the model to be used in a form (not a view) it does seem to work okay, I see the correct inputs in the form coming from the database. The error happens when I query using Django - this query is setup within the same model 'TableName' itself, is that referenced correctly? (instead of doing importing TableName from app-name.models)
TableName.objects.using('CUSTOMDBSETTING').filter(colid='value')
Note that this works when I try using the shell directly, just not through Django. I also don't have a primary key for this table, and when I try setting up a non-default (not id), it gives me another error.
So it does give me the error when I try to reference what should be the primary key (but isn't) in the table mapping. When I try removing the primary key for the specific id (only in models, not in the migrations)
('42S22', "[42S22] [Microsoft][ODBC Driver 17 for SQL Server][SQL
Server]Invalid column name 'id'. (207) (SQLExecDirectW); [42S22]
[Microsoft][ODBC Driver 17 for SQL Server][SQL Server]Statement(s)
could not be prepared. (8180)")
So, the answer to this question is that if one doesn't assign a specific unique/primary key in the model in question, the default 'id' mapped in Django would apply.
That is, expect your migrations to have a primary key like this (the model mapping wouldn't explicitly show this):
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
...
]
To fix this issue, either use the primary key 'id' from Django or setup a primary key - doesn't have to done for the actual table, just the Django model. Then re-run the migrations and make sure they applied to catch this change...
Now, migrations should look like this (and no 'id' key would be used)
('tableid', models.CharField(blank=True, db_column='TableID', max_length=255, primary_key=True, serialize=False)),
or something like this if it's auto incremented
customid = models.AutoField(db_column='CustomID', null=False, primary_key=True, max_length=10)
I have a model with a foreign key field. I want to remove the foreign key reference. However, migration fails because of the following error:
Cannot delete or update a parent row: a foreign key constraint fails
I understand what is going on, but I don't know how to resolve this properly with Django. Right now (since I'm at the beginning of my project), I go into the MySQL database manually and delete the tables and re-migrate as if it was the first migration.
Is there a way, using Django, to get around this foreign key constraint issue?
I'm using Django 1.7
Database backend is MySQL
EDIT - Models Before/After migrations
Before:
class Skills(models.Model):
# fields here...
class Project(models.Model):
skills = models.ForeignKey(Skills, verbose_name = "Required Skills", blank = True, null = True)
After:
class Skill(models.Model):
# fields here...
class Project(models.Model):
skills = models.ForeignKey(Skill, verbose_name = "Required Skills", blank = True, null = True)
I'm pretty sure all I've done is removed the "Plural" from the Skill model. The the makemigrations command works fine, but the migrate command fails with the above noted error.
EDIT 2
Hit the same error:
Cannot drop column 'skills_id': needed in a foreign key constraint 'projects_project_skills_id_4cc7e00883ac4de2_fk_projects_skill_id'
This time I dropped the field skill from model Project
A little hack I used:
Add a migration operations that first alters the field to say IntegerField before other operations i.e
operations = [
migrations.AlterField(
model_name='projects_project',
name='skills',
field=models.IntegerField(default=0)
),
..... other migration entries now after this.
]
This is my SOLUTION (do this manually first)
ALTER TABLE forms_formentry DROP FOREIGN KEY
forms_formentry_form_id_d0f23912_fk_forms_form_page_ptr_id
This is the ERROR
django.db.utils.OperationalError: (1833, "Cannot change column 'page_ptr_id': used in a foreign key constraint 'forms_formentry_form_id_d0f23912_fk_forms_form_page_ptr_id' of table 'dbname.forms_formentry'")
I have a unmanaged model Client
class Client(models.Model):
client_id = models.IntegerField('ID', primary_key=True)
name = models.CharField()
class meta:
manage = False
I use Client to extend User:
class Account(models.Model):
user = models.OneToOneField(User)
client = models.OneToOneField(Client, to_field='client_id')
I thought Django will create foreign key reference to Client.client_id instead of Client.id even without explicit to_field='client_id' because of the definition of Client model. But sqlmigrate shows:
BEGIN;
CREATE TABLE `pubsite_account` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `deg_client_id` integer NOT NULL UNIQUE, `user_id` integer NOT NULL UNIQUE);
ALTER TABLE `pubsite_account` ADD CONSTRAINT pubsite_account_deg_client_id_73086fddd308cd6f_fk_Clients_id FOREIGN KEY (`deg_client_id`) REFERENCES `Clients` (`id`);
ALTER TABLE `pubsite_account` ADD CONSTRAINT pubsite_account_user_id_33ed558985f73b32_fk_auth_user_id FOREIGN KEY (`user_id`) REFERENCES `auth_user` (`id`);
COMMIT;
And nothing changes with setting to_field='client_id'. Any thoughts would be appreciated.
Django version: 1.8, 1.7 (Yep, I tried both)
Databse: mysql
I cant try right now but I think db_column is what you are looking for
client = models.OneToOneField(Client, db_column='client_id')
After discuss in #django-dev, it turns out to be a bug.
While a ticket has been opened, a developer MarkusH offers a temporary work around:
change managed = False on the model to =True
once you have new migration files, change the managed option on the model back to False
and in the corresponding migration file add 'managed': False, to the options array in the CreateModel operation.
-------edit---------
From the ticket page, this bug has been fixed. So if you come to here for the same thing, just update Django.
I'm attempting a migration from MySQL to PostgreSQL, and now I'm having trouble with concrete base classes. I have code similar to this:
class BaseKlass(models.Model):
name = CharField(max_length = 64)
class SomeKlass(BaseKlass):
value = IntegerField()
Whenever I create an instance of SomeKlass, I get an error like this:
IntegrityError: null value in column "baseklass_ptr_id" violates not-null constraint
I looked at the SQL being executed, and indeed the value for baseklass_ptr_id was null.
Any idea?
Setting an owner for the id sequence solved the problem:
ALTER SEQUENCE myapp_baseklass_id_seq OWNED BY myapp_baseklass.id;