Django implementation of default value in database - python

I had a field on a model with was:
class SomeModel(models.Model):
some_field = models.CharField(max_length=10, null=True, blank=True)
Then I changed my model to:
class SomeModel(models.Model):
some_field = models.CharField(max_length=10, default='')
When I ran django-admin sqlmigrate somemodels somemigration to check my migration I found the following changes:
ALTER TABLE "somemodels" ALTER COLUMN "some_field" SET DEFAULT '';
UPDATE "somemodels" SET "some_field" = '' WHERE "some_field" IS NULL;
ALTER TABLE "somemodels" ALTER COLUMN "some_field" SET NOT NULL;
ALTER TABLE "somemodels" ALTER COLUMN "some_field" DROP DEFAULT;
I am not understanding why the Django apply a DROP DEFAULT in the table since I am creating a default value. If this is correct, how does Django implement the default values?
Information about my tools:
Postgresql 9.5;
Django 1.11b1;

The comments to django/db/backends/base/schema.py, starting ln. 571, detail the steps involved here:
When changing a column NULL constraint to NOT NULL with a given default value, we need to perform 4 steps:
Add a default for new incoming writes
Update existing NULL rows with new default
Replace NULL constraint with NOT NULL
Drop the default again.
Django does not usually use the built-in SQL default to set values (remember that Django can use callable values for defaults). You can find more information in this rejected bug report.

Related

django.db.utils.IntegrityError: null value in column "id" violates not-null constraint

I want to save data from the Django model to PostgreSQL database with:
mymodel.objects.create(title='test')
this model only has title and id but it raises this error:
django.db.utils.IntegrityError: null value in column "id" violates not-null constraint
how can I fix it? why id is not set automatically as always?
If you somehow had your ID field altered on the database level and you want to make it an autoincrementing sequence again do this
In the below example check what mymodel's table will be in Postgres in the example below its called mytable
// Pick a starting value for the serial, greater than any existing value in the table
SELECT MAX(id)+1 FROM mytable
// Create a sequence for the serial (tablename_columnname_seq is a good name)
CREATE SEQUENCE mytable_id_seq MINVALUE 3 (assuming you want to start at 3)
// Alter the default of the column to use the sequence
ALTER TABLE test ALTER id SET DEFAULT nextval('mytable_id_seq')
// Alter the sequence to be owned by the table/column;
ALTER SEQUENCE mytable_id_seq OWNED BY mytable.id
REF: Changing primary key int type to serial
You should allow Django to create the id as the primary key instead of explicitly putting it in your model. You could call it something else like mymodel_id if you need it as a separate field.
Example:
class Book(models.Model):
title = models.CharField(null=False, blank=False)
def __str__(self):
return str(self.id)
After that run:
python manage.py makemigrations
python manage.py migrate
If you need to integrate Django with an existing database you can try this: Integrating Django with an existing database

Django - IntegrityError in terminal when migrating changes to a relationship field

I've got a Django model like the following..
class ExampleModel(models.Model):
name = models.CharField(...)
related_user = models.ForeignKey(UserTypeA, related_name='related_example', blank=True, null=True, on_delete=models.SET_NULL)
where I recently had to make a change to the related_user field by changing the ForeignKey from UserTypeA to UserTypeB.
Of course, this raises an error in the terminal when I attempt to python manage.py makemigration...
django.db.utils.IntegrityError: insert or update on table "models_examplemodel" violates foreign key constraint "models_examplemodel_related_user_id_ac0c6018_fk_accounts_"
DETAIL: Key (related_user_id)=(13) is not present in table "accounts_usertypea".
What's the safest way to go about making these changes? Currently I'm in development so I'm happy to delete my data/migrations/whatever, but I imagine in production this would be difficult.
The ideal behaviour I'd like to see here is the relations from ExampleModel and UserTypeA just being deleted, and so the current relationships would be set to NULL. Thoughts?
if you simply want to drop UserTypeA and use UserTypeB with None values simply do this:
remove related_user field
generate migrations
add related_user field
generate migrtions
If you want to do something more complecated (fill UserTypeB based on UserTypeA) these are the steps
add realted_user_b field with default as null
generate migration file
write a data migrations file which fills realted_user_b based on current data docs
remove realted_user field
generate migration file
rename realted_user_b to realted_user
generate migration file

Django cannot insert duplicate key value NULL

I need to allow NULL in enc_id, but if values are not null, I need those values to be unique. Here's my model:
class Intake(models.Model):
id = models.AutoField(primary_key=True)
enc_id = models.IntegerField(blank=True, null=True, unique=True)
enc_date = models.DateField(null=True)
enrollment = models.ForeignKey('Enrollment')
Error when trying to add another instance without an enc_id:
django.db.utils.IntegrityError: ('23000', "[23000] [FreeTDS][SQL Server]Violation of UNIQUE KEY constraint 'UQ__caseload__E136D21F4222D4EF'. Cannot insert duplicate key in object 'dbo.caseload_intake'. The duplicate key value is (<NULL>). (2627) (SQLExecDirectW)")
According to what I've read (including this and a few resolved Django issues), having blank=True, null=True, unique=True should allow me to have duplicate NULLs, but no go. I've recreated my DB just in case and it still raises the integrity error.
I'm running Django 1.10 and MS SQL Server 10. Any ideas?
For those who come up against this in the future - this is a MS SQL Server limitation. ANSI standards require that a UNIQUE index contains no duplicate values - SQL Server (but not other databases) consider NULL is equal to NULL - many other databases (e.g. Postgres) and the ANSI standard from 92 onward consider NULL to be an unknowable value, which is not the same as a different unknowable value -- consider in SQL SELECT WHERE __ IS NULL vs WHERE ___ = NULL. SQL Server has an ANSI_NULLS flag, which in effect forces the standards-compliant use of IS (NOT) NULL rather than xyz =/<> NULL in queries, but this doesn't have the same effect in indexes.
More non-Django-specific info here: https://www.sqlservergeeks.com/sql-server-unique-constraint-multiple-null-values/
Rosie,
it doesn't make a lot of sense to me to have a unique field where you can have duplicate NULLs. What about you remove the constraint and reinforce this rule by other means?
For example, you could override the method save in order to do that..
def save(self, *args, **kwargs):
# place your logic here
I hope this makes sense, but a rule at DB level such as the one you are defining is extremely limiting..

Django Migration Error with MySQL: BLOB/TEXT column 'id' used in key specification without a key length"

We have Django Model, use Binary Field for ID.
# Create your models here.
class Company(models.Model):
id = models.BinaryField(max_length=16, primary_key=True)
name = models.CharField(max_length=12)
class Meta:
db_table = "company"
We use MySQL Database and have error when migrate.
File "/home/cuongtran/Downloads/sample/venv/lib/python3.5/site-packages/MySQLdb/connections.py", line 270, in query
_mysql.connection.query(self, query)
django.db.utils.OperationalError: (1170, "BLOB/TEXT column 'id' used in key specification without a key length")
Do you have any solution? We need to use MySQL and want to use the Binary Field for ID.
Thank you!
I think you cannot achieve this. Based on Django documentation it looks like use of binary fields is discouraged
A field to store raw binary data. It only supports bytes assignment.
Be aware that this field has limited functionality. For example, it is
not possible to filter a queryset on a BinaryField value. It is also
not possible to include a BinaryField in a ModelForm.
Abusing BinaryField
Although you might think about storing files in the database, consider
that it is bad design in 99% of the cases. This field is not a
replacement for proper static files handling.
And based on a Django bug, it is most likely impossible to achieve a unique value restriction on a binary field. This bug is marked as wont-fix. I am saying most likely impossible as I did not find evidence to confirm that binary field is stored as a BLOB field but the error does allude to it.
Description
When I used a field like this:
text = models.TextField(maxlength=2048, unique=True)
it results in the following sql error when the admin app goes to make the table
_mysql_exceptions.OperationalError: (1170, "BLOB/TEXT column 'text' used in key specification without a key length")
After a bit of investigation, it turns out that mysql refuses to use unique with the column unless it is only for an indexed part of the text field:
CREATE TABLE `quotes` ( \`id\` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `text` longtext NOT NULL , \`submitTS\` datetime NOT NULL, `submitIP` char(15) NOT NULL, `approved` bool NOT NULL, unique (text(1000)));
Of course 1000 is just an arbitrary number I chose, it happens to be the maximum my database would allow. Not entirely sure how this can be fixed, but I figured it was worth mentioning.
MySQL restricts the primary key on BLOB/TEXT column to first N chars, when you generates migration file using Django's makemigrations command, BinaryField in Django is mapped to longblob which is BLOB column in MySQL without specifying the key length.
Which means your Django model definition :
class Company(models.Model):
id = models.BinaryField(max_length=16, primary_key=True)
name = models.CharField(max_length=12)
class Meta:
db_table = "company"
will be converted to SQL expression that causes this error (You can check out the detailed SQL expressions by sqlmigrate command) :
CREATE TABLE `company` (`id` longblob NOT NULL PRIMARY KEY,
`name` varchar(12) NOT NULL);
while the correct SQL expression for MySQL should be like this :
CREATE TABLE `company` (`id` longblob NOT NULL,
`name` varchar(12) NOT NULL);
ALTER TABLE `company` ADD PRIMARY KEY (id(16));
where PRIMARY KEY (id(16)) comes from your id length in the BLOB column, used to structure primary key index of the table.
So the easiest solution is as described in the accepted answer -- avoid BinaryField in Django as primary key, or you can manually add raw SQL scripts to your migration file if you really need BinaryField (BLOB column) to be primary key and you are sure the id field will NOT go beyond the specific size (in your case, 16 bytes).

In python how to update psql Table column from updated peewee schema

I have a tabe in psql database with following property:
author_ids = ArrayField(null=False)
I want to update this table's column with following property:
author_ids = ArrayField(IntegerField, null=False, index=True)
I am using Peewee's migration but there is nothing for updating columns.
You're right, there is no API in Schema Migrations for modifying a column's type. But from ArrayField, I'd say the default is already IntegerField, so no change here.
This leaves index = True as the only change, which is covered by add_index.

Categories

Resources