Using south to migrate database table - mysql

I was not using south. Now I want to add a couple columns. Am I screwed?
(env)noah:code broinjc$ ./manage.py schemamigration reports --initial
Creating migrations directory at '/Users/broinjc/esp/code/reports/migrations'...
Creating __init__.py in '/Users/broinjc/esp/code/reports/migrations'...
+ Added model reports.Classroom
+ Added model reports.Student
+ Added model reports.SurveySet
+ Added model reports.Survey
Created 0001_initial.py. You can now apply this migration with: ./manage.py migrate reports
(env)noah:code broinjc$ ./manage.py migrate reports
Running migrations for reports:
- Migrating forwards to 0001_initial.
> reports:0001_initial
FATAL ERROR - The following SQL query failed: CREATE TABLE "reports_classroom" ("id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "added" datetime NOT NULL, "updated" datetime NOT NULL, "name" varchar(30) NOT NULL, "am_or_pm" varchar(2) NOT NULL)
The error was: table "reports_classroom" already exists
! Error found during real run of migration! Aborting.
! Since you have a database that does not support running
! schema-altering statements in transactions, we have had
! to leave it in an interim state between migrations.
! You *might* be able to recover with: = DROP TABLE "reports_classroom"; []
= DROP TABLE "reports_student"; []
= DROP TABLE "reports_surveyset"; []
= DROP TABLE "reports_survey"; []
! The South developers regret this has happened, and would
! like to gently persuade you to consider a slightly
! easier-to-deal-with DBMS (one that supports DDL transactions)
! NOTE: The error which caused the migration to fail is further up.
Error in migration: reports:0001_initial
After seeing all that, I thought, maybe I need to update my models (making them inconsistent with sqlite db) So I updated them and then ran the same command but with --auto instead of initial...
(env)noah:code broinjc$ ./manage.py schemamigration reports --auto
? The field 'SurveySet.top_num' does not have a default specified, yet is NOT NULL.
? Since you are adding this field, you MUST specify a default
? value to use for existing rows. Would you like to:
? 1. Quit now, and add a default to the field in models.py
? 2. Specify a one-off value to use for existing columns now
... So I went ahead with option 2, and then proceeded to migrate...
(env)noah:code broinjc$ ./manage.py migrate reports
Running migrations for reports:
- Migrating forwards to 0002_auto__add_field_surveyset_top_num__add_field_surveyset_externalizer_ra.
> reports:0001_initial
FATAL ERROR - The following SQL query failed: CREATE TABLE "reports_classroom" ("id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "added" datetime NOT NULL, "updated" datetime NOT NULL, "name" varchar(30) NOT NULL, "am_or_pm" varchar(2) NOT NULL)
The error was: table "reports_classroom" already exists
! Error found during real run of migration! Aborting.
! Since you have a database that does not support running
! schema-altering statements in transactions, we have had
! to leave it in an interim state between migrations.
! You *might* be able to recover with: = DROP TABLE "reports_classroom"; []
= DROP TABLE "reports_student"; []
= DROP TABLE "reports_surveyset"; []
= DROP TABLE "reports_survey"; []

I'll try and explain what's going on so you better understand how to do what you want yourself.
Prior to using south you have some tables in your database which were generated from your models when you first run syncdb.
If you change your model, say you add a field "my_field", Django will fail when trying to read/write to it, since the table doesn't contain a column named "my_field". You'd normally have to dump your entire table and recreate it with syncdb. I'm sure you don't want to do that since you already have some data in you DB.
Say you want to make some changes without losing the data. First, you need to "convert" your app to south.
Basically, when you run schemamigration --initial, South will create a script (0001_initial.py) to replicate the current state of your models into a database.
If you run that script via manage.py migrate reports, it'll try to recreate all the tables you had initially, but in your case, since your DB already contains those tables, it'll scream at you saying the tables already exist:
FATAL ERROR - The following SQL query failed: CREATE TABLE "reports_classroom" ("id" integer NOT NULL PRIMARY KEY, "user_id" integer NOT NULL, "added" datetime NOT NULL, "updated" datetime NOT NULL, "name" varchar(30) NOT NULL, "am_or_pm" varchar(2) NOT NULL)
The error was: table "reports_classroom" already exists
The way to make South believe you have already applied that migration, you use the --fake option.
manage.py migrate reports 0001 --fake
Which is like saying, go to the migration state 0001_initial (you only have to write the numeric part of the name), but don't actually apply the changes.
After doing that, say you add a new field "my_field_02" to one of your models. As before, Django is referencing a field that doesn't exist in your model's table. To create it without writing the SQL yourself, you do:
manage.py schemamigration reports --auto
Which will create a new migration called something like 0002_auto__add_my_field_02.py which you then need to apply via manage.py migrate reports. You could also say manage.py migrate reports 0002 to specify the migration state you want to go to, but by default South will try to apply all the following migrations (remember you're already at state 0001).
I highly recommend you read South's documentation and backup your production data prior to doing anything.
tl;dr Read this and backup your data.

Related

Unable to create or change a table without a primary key - Laravel DigitalOcean Managed Database

I've just deployed my app to DigitalOcean using (Managed Database) and I'm getting the following error when calling php artisan migrate
SQLSTATE[HY000]: General error: 3750 Unable to create or change a
table without a primary key, when the system variable 'sql_require_primary_key'
is set. Add a primary key to the table or unset this variable to avoid
this message. Note that tables without a primary key can cause performance
problems in row-based replication, so please consult your DBA before changing
this setting. (SQL: create table `sessions` (`id` varchar(255) not null,
`user_id` bigint unsigned null, `ip_address` varchar(45) null,
`user_agent` text null, `payload` text not null, `last_activity` int not null)
default character set utf8mb4 collate 'utf8mb4_unicode_ci')
It appears that Laravel Migrations doesn't work when mysql var sql_require_primary_key is set to true.
Do you have any solutions for that?
From March 2022, you can now configure your MYSQL and other database by making a request to digital ocean APIs.
Here's the reference: https://docs.digitalocean.com/products/databases/mysql/#4-march-2022
STEPS TO FIX THE ISSUE:
Step - 1: Create AUTH token to access digital ocean APIs. https://cloud.digitalocean.com/account/api/tokens
STEP - 2: Get the database cluster id by hitting the GET request to the below URL with bearer token that you have just generated above.
URL: https://api.digitalocean.com/v2/databases
Step - 3: Hit the below URL with PATCH request along with the bearer token and payload.
URL: https://api.digitalocean.com/v2/databases/{YOUR_DATABASE_CLUSER_ID}/config
payload: {"config": { "sql_require_primary_key": false }}
That's all. It worked flawlessly.
For more information, please refer to API DOCS:
https://docs.digitalocean.com/products/databases/mysql/#latest-updates
I was trying to fix this problem with an import to DO Managed MySQL using a mysqldump file from a WordPress installation. I found adding this to the top of the file did work for my import.
SET #ORIG_SQL_REQUIRE_PRIMARY_KEY = ##SQL_REQUIRE_PRIMARY_KEY;
SET SQL_REQUIRE_PRIMARY_KEY = 0;
I then imported using JetBrains DataGrip and it worked without error.
Add in your first migration:
\Illuminate\Support\Facades\DB::statement('SET SESSION sql_require_primary_key=0');
Inside: Schema::create() function.
Just add set sql_require_primary_key = off
Like this
to your SQL file.
One neat solution is defined here. The solution is to add listeners to migrate scripts and turn sql_require_primary_key on and off before and after executing a migration. This solution solve the problem where one is unable modify migrations script such as when they are from a library or a framework like Voyager.
<?php
namespace App\Providers;
use Illuminate\Database\Events\MigrationsStarted;
use Illuminate\Database\Events\MigrationsEnded;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider {
/**
* Register any application services.
*
* #return void
*/
public function register() {
// check this one here https://github.com/laravel/framework/issues/33238#issuecomment-897063577
Event::listen(MigrationsStarted::class, function (){
if (config('databases.allow_disabled_pk')) {
DB::statement('SET SESSION sql_require_primary_key=0');
}
});
Event::listen(MigrationsEnded::class, function (){
if (config('databases.allow_disabled_pk')) {
DB::statement('SET SESSION sql_require_primary_key=1');
}
});
}
// rest of the class
}
For bigger sql file, can with this command (nano editor can open in 1 week if your file size is <8GB, lol):
First :
sed -i '1i SET SQL_REQUIRE_PRIMARY_KEY = 0;' db.sql
Second :
sed -i '1i SET #ORIG_SQL_REQUIRE_PRIMARY_KEY = ##SQL_REQUIRE_PRIMARY_KEY;' db.sql
According to the MySQL documentation purpose of this system variable is
to avoid replication performance issues: "Enabling this variable helps avoid performance problems in row-based replication that can occur when tables have no primary key."
IMHO, there are two possible options to consider for your problem;
Add primary key to this and every table in your migration, including temporary tables. This one is better and i think more convenient way to do it since there is no drawback to have primary key for each table.
Whether statements that create new tables or alter the structure of existing tables enforce the requirement that tables have a primary key.
Change your provider because according to here "We support only MySQL v8."
Also here is the bug report
I contacted DigitalOcean via a ticket to ask if they want to disable the requirement and they did the next day :)
So you can just ask them
Thanks for getting in touch with us!
I understand you will like to disable the primary requirement on your managed database. The primary requirement for your managed database ****** has been disabled
Unfortunately, we can't change the sql_require_primary_key value in the digital ocean MySQL database. instead, you can set the id to the primary key just by adding primary()
When enabled, sql_require_primary_key has these effects:
Attempts to create a new table with no primary key fail with an error. This includes CREATE TABLE ... LIKE. It also includes CREATE TABLE ... SELECT, unless the CREATE TABLE part includes a primary key definition.
Attempts to drop the primary key from an existing table fail with an error, with the exception that dropping the primary key and adding a primary key in the same ALTER TABLE statement is permitted.
Dropping the primary key fails even if the table also contains a UNIQUE NOT NULL index.
Attempts to import a table with no primary key fail with an error.
Default value is OFF , but in your case you need to set OFF from ON
IMPORTANT LINK
HOW TO SET
If you're importing in some SQL client, just run this query on that particular database before importing.
set sql_require_primary_key = off
Works all good for DO managed Mysql Database. Cheers!
add this line to your migration file.
$table->increments('aid');

How to resolve django.db.utils.IntegrityError: (1364, "Field 'name' doesn't have a default value")

I'm getting this error from trying to create a superuser for my Django project. Unsure what table requires a default value for its 'name' column.
After successfully creating migrations for my Django project I ran python manage.py createsuperuser to create the superuser and got the following error:
django.db.utils.IntegrityError: (1364, "Field 'name' doesn't have a default value"). I installed mysql (8.0) am using homebrew on OSX and using python 3 in a virtual env.
I'm not sure which database the above command tries to engage, talk less of which table. In any case I have gone through all tables in the db relevant to my project as well as in the mysql database and have run this command on the only name column found:
ALTER TABLE django_migrations ALTER COLUMN name SET DEFAULT '-'
But I am still getting this error. I have read up on createsuperuser in the Django docs as well as looked into some of the Django code but have gleaned very little of value to solving this. Any help with this would be greatly appreciated.
Try:
ALTER TABLE django_content_type MODIFY COLUMN name character varying(50) NOT NULL DEFAULT 'not null';
Or if you want it to be empty:
ALTER TABLE django_content_type MODIFY COLUMN name character varying(50) NOT NULL DEFAULT '';
Taken from here: https://github.com/arteria/django-background-tasks/issues/139
By default Django has a table called auth_user serving the user authentication which doesn't contain a field called name, so my assumption is that you have a custom AUTH_USER_MODEL defined in your settings.py which contains a field called name with not set default value.

Django AutoField default value error

Django 1.7.1, MySQL 5.6, Python 2.7.8
I had a model that looked like this:
class Host(models.Model):
hostName = models.CharField(max_length=45, primary_key=True)
...
I removed primary_key=True manually, which caused manage.py sqlmigrate to show that the primary key was being dropped, an autoincrementing 'id' column was being added, and it was getting the primary key. I was prompted for a default value for the new 'id' column, and mistakenly gave it 1, which was already in the table. The relevant SQL read:
ALTER TABLE `Host` ADD COLUMN `id` integer AUTO_INCREMENT DEFAULT 1 NOT NULL PRIMARY KEY;
and the migration code included:
operations = [
migrations.AddField(
model_name='host',
name='id',
field=models.AutoField(auto_created=True, primary_key=True, default=1, serialize=False, verbose_name='ID'),
preserve_default=False,
),
The result was that I can still modify my models and makemigrations works, but each migrate command gives this error:
django.db.utils.OperationalError: (1067, "Invalid default value for 'id'")
and does not take effect.
I've tried reverting the primary key change, manually pointing the immediately subsequent migration at the immediately previous migration (which raised consistency problems), and migrating explicitly to the immediately previous migration. What I would really like to do is simply erase/ignore the problem migration. I would also be satisfied with suppressing the error since I have since reverted the change. How can I do either of these?
Edit:
Also, if an autoincrement column can never have a default value why does Django allow itself to pass the SQL with ...AUTO_INCREMENT DEFAULT 1...?
You can fix the error in SQL (if you haven't already) and then just run ./manage.py migrate [your_app_name] [the_migration_number] --fake
The --fake will tell Django to pretend it ran the migration and it will not try to run it again.
Just be sure that the changes you make in SQL are accurately reflected in the fake migration, because Django does not compare the models to the database when you run makemigrations.
Regarding the error about the id column, if I remember correctly, you cannot assign a default value to a mysql auto increment field.
You cannot set default value for Auto Increment Columns in MySQL.
You have to change your DB table schema. The problem is that your Primary Key "id" (Auto Generated by Django) has Auto Increment = "True". As Auto increment is true you are unable to set default value to 1. Each time you run your migration script it throws error because of that
For Example if you run following query
ALTER TABLE YOU_TABLE CHANGE id id INT(11) AUTO_INCREMENT NOT NULL DEFAULT 1;
It will throw an error an error
MySQL Database Error: Invalid default value for 'id' 1
So you have to change your Primary key to have no default value.
ALTER TABLE YOU_TABLE CHANGE id id INT(11) AUTO_INCREMENT NOT NULL;
This way the schema will be changed and you will no longer get this error.
Also in Django you can set default value like this
column = models.CharField(max_length=7, default='0000000', editable=False)
For Django Reference: https://docs.djangoproject.com/en/dev/ref/models/fields/#editable
StackOverflow Question: Default value for field in Django model
primary_key can't have this default parameter, so you should delete this default parameter.

Field 'id' doesn't have a default value django-registration

I recently loaded all of the django-registration tables from sqlite into a mysql database. It works fine for logging in users, but now when I try to register a user I get this error:
Warning at /accounts/register/
Field 'id' doesn't have a default value
I tried using this line to make the id field auto increment but I'm still getting the error.
ALTER TABLE auth_user MODIFY id INT(12) NOT NULL AUTO_INCREMENT;
Thanks for the help.
Edit: I ended up deleting all of the dables and using manage.py syncdb to recreate them although that isn't really a solution.
You probably have incorrect table structure. Try to:
Create test project and setup connection to test blank database.
Then try to do manage.py syncdb on test project.
Compare table auth_user structure between current and test project. For example compare create table script for both projects.

Migrate Django / MySQL foreign key to accept null values

Have a Django / MySQL set up. There's a model, Survey, which currently looks like...
class Survey(models.Model):
company = models.ForeignKey('Company')
I want to set up the model, so company can be a null value:
company = models.ForeignKey('Company', blank = True, null = True)
However, I'm not sure what I should do on the MySQL side to ensure all the existing constraints / models. Do I just alter the column through the console to accept null values? It's a live database, so I don't want to experiment too much (my development environment uses SqlLite3).
Update your model so that blank=True, null=True. Then run the sqlall command on your production server (so that it gives the output for MySQL)
./manage.py sqlall myapp
Find the create table statement This will show the new definition for the survey_id field.
CREATE TABLE `myapp_survey` (
...
`survey_id` integer
...
Then, in your database shell, modify the column to accept null values using the ALTER TABLE command.
ALTER TABLE myapp_survey MODIFY company integer;
Be careful, and consider whether you want to run MySQL in your development environment as well. Do you really want to be copying and pasting commands from Stack Overflow into your live DB shell without testing them first?