Integrity Error Not Null Constraint Failed When Attempting to Migrate - django

I am receiving this error when attempting to migrate:
"return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: NOT NULL constraint failed: unit_manifests.product_name_id"
This is in reference to the product_name field in the model below.
1) Why do I need to set a default value for a foreign key field?
2) I initially ran without default = none, blank = true, null = true. I have now run makemigrations again but when I migrate I still get the error, I believe it is attempting to run an old migrations first. How can I get around this?
MODELS.PY
class Manifests(models.Model):
reference = models.ForeignKey(Orders)
cases = models.IntegerField()
product_name = models.ForeignKey(Products, default=None, blank=True, null=True)
count = models.IntegerField()
CNF = models.DecimalField(max_digits=11, decimal_places=2, default=None, blank=True, null=True)
FOB = models.DecimalField(max_digits=11, decimal_places=2, default=None, blank=True, null=True)
def __str__(self):
return self.description

Django migrations works like this.
First you run makemigrations which will create a file based on changes from the last file generated from running makemigrations.
Then you run migrate which will push the changes in the migrations folder which are not yet registered in the database in table 'django_migrations'. The migration file names are important because django uses it to identify the migration.
If you want to fake a migration, you can manually create a database instance in the migrations table with the migrations file you want to fake.
Lets say the migration file is called '0002_auto_20190212_1240.py', then you insert a row in the 'django_migrations' table with app=[app_name], name=0002_auto_20190212_1240 and applied=[+1 second after last instance].

Related

Database error during the test: cannot change column

I changed one field of the model, to create migrations and applied it. There were no errors. But when I try to run the tests, there's an error.
django.db.utils.OperationalError: (1833, "Cannot change column 'rfiid': used in a foreign key constraint 'elements_attachments_rfi_id_bc723558_fk_rfis_rfiid' of table 'test_smap_production.elements_attachments'")
field that I changed - rfiid. I switched it from AutoField to CharField.
models.py
class Rfis(models.Model):
rfiid = models.CharField(max_length=6, primary_key=True)
....
The migration was successful, and there is no already created instance of the model in the database. Why such an error occurs and how to correct it?
p.s.
class ElementsAttachments(models.Model):
e = models.ForeignKey('Elements', models.DO_NOTHING)
attachment = models.ForeignKey('Attachments', models.DO_NOTHING)
rfi = models.ForeignKey('Rfis', models.DO_NOTHING)
timestamp = models.DateTimeField(auto_now=True)
active = models.IntegerField()
vendor_response = models.IntegerField(blank=True, null=True)
A not clean way and risky is to delete all previous migrations and make a new migration.
Warning: This is risky and can provoke damages if you already have data in your databases.

Django - Existing DB Views and Foreign Keys

I have a simple view on the DB which selects from other DB's tables located on the same MSSQL Server to ultimately serve the collected info as a dropdown to the user.
So far I've added the Model with inspectdb:
class AutPricePlanView(models.Model):
priceplan_name = models.CharField(db_column='PricePlan', max_length=50, blank=True, unique=True)
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'AUT_PricePlanView'
Also I have a second existing (Django Native) Model where I want to use the values from the view for a Dropdown Field (to keep everything in sync):
class PricePlanDownload(models.Model):
requesting_user = models.CharField(blank=True, default=None, max_length=50, null=True)
requested_at = models.DateTimeField(auto_now_add=True)
document = models.FileField(upload_to='documents/price_plan_uploads/%Y/%m/%d', blank=True)
priceplan = models.ForeignKey(AutPricePlanView, null=True, on_delete=models.DO_NOTHING)
Makemigrations works fine but when I try to actually migrate I get the following issue: (shortened it a little bit)
django.db.utils.ProgrammingError: ('42000', "[42000] [FreeTDS][SQL Server]Foreign key references object 'AUT_PricePlanView' which is not a user table. (1768) (SQLExecDirectW)")
I would be really grateful if someone had an idea or a workaround since I can't figure out what the heck this has to do with a "user" table...
Since the view is not actually a table, you cannot set Foreign Key constraints. Since ForeignKey's default db_constraint value is True, Django tries to set Foreign Key constraints when performing migrations. This is the reason the migration fails.
So, you can turn off the db_constraint option. And you can remove the existing migration file, and re-create the migration file. Then, the migration will success and you can keep everything in sync.
class PricePlanDownload(models.Model):
... other fields ...
priceplan = models.ForeignKey(AutPricePlanView, null=True, on_delete=models.DO_NOTHING, db_constraint=False)
Pro Tip: You can review migration's SQL using python manage.py sqlmigrate <appname> <migration number>, like python manage.py sqlmigrate yourapp 0002.
Update: You can define __str__ to display the correct value at the dropdown menu.
class AutPricePlanView(models.Model):
priceplan_name = models.CharField(db_column='PricePlan', max_length=50, blank=True, unique=True, primary_key=True)
# null=False by default. See https://github.com/django/django/blob/master/django/db/models/fields/__init__.py#L132
def __str__(self):
return self.priceplan_name
class Meta:
managed = False # Created from a view. Don't remove.
db_table = 'AUT_PricePlanView'

You are trying to add a non-nullable field stock without a default

I have added a new field in my model but after that I have deleted db.sqlite3 (to ensure I don't get error below)
agrawalo#:~/myapp> ls
README.md config core manage.py requirements.txt
But still I get this error when I run makemigrations
agrawalo#:~/myapp> ./manage.py makemigrations
You are trying to add a non-nullable field 'high52' to stock without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
class Stock(models.Model):
name = models.CharField(max_length=255)
code = models.CharField(max_length=20, db_index=True)
price = models.DecimalField(max_digits=19, decimal_places=2)
diff = models.DecimalField(max_digits=19, decimal_places=2)
open_price = models.DecimalField(max_digits=19, decimal_places=2)
previous_close = models.DecimalField(max_digits=19, decimal_places=2)
low52 = models.DecimalField(max_digits=19, decimal_places=2)
high52 = models.DecimalField(max_digits=19, decimal_places=2)
last_updated = models.DateTimeField()
objects = DataFrameManager()
def save(self, *args, **kwargs):
''' On save, update timestamps '''
self.last_updated = timezone.now()
return super(Stock, self).save(*args, **kwargs)
def __str__(self):
return str(self.code)
low52 and high52 are the newly added fields. Please note that none of the other existing field throw this error.
You can either provide a default value to the field
high52 = models.DecimalField(max_digits=19, decimal_places=2, default=0.0)
or you can make it optional
high52 = models.DecimalField(max_digits=19, decimal_places=2, null=True, blank=True)
You can make a decision based on your choice.
To answer your question about the error, the previously existing fields might have been created in the initial migration itself and they don't need a default value. But for the newly added field, you need a default value for mandatory fields and this default value will be populated in the existing records. This doesn't depend on whether you have deleted the existing database or not. This depends on the current state of migrations for that model. Since this is not the initial migration, you will need to provide a default value or make it optional.
It doesn't matter if you deleted the database file or not. makemigrations does not check the database.
You can only add a non-nullable field to a model if you add it to a new model and make an initial migration. This is because, after you make that initial migration, Django has no way of knowing whether you deployed your application somewhere else, so it has no way of knowing if there are instances of a model out there. A situation where this would go wrong:
Create a model X and makemigrations on your local machine.
Deploy your Django application to a server, where the database is populated with instances of model X.
Delete your local database, add non-nullable field Y to model X, makemigrations.
Deploy you Django application to the server.
Problems occur.
The solution here is to either:
Set the Field to null=True
Add a default to the model.
Provide a default when making the migrations.
In your situation, I would say it is ok to provide a one-off default, because it sounds like you have no populated database yet.
You need to provide blank and null True for high52 field .
high52 = models.SomeField(blank=True,null=True)
If you don't want so then you can select any of these two options.
For example If high52 is CharField then you can choose the 1 option and provide some value like '..' or you can set defaults in your models.py

Django South - Change Foreign Key to Required

I changed my model from this:
class DistList(models.Model):
creator = models.ForeignKey(User, related_name='creator')
created_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=200, unique=True)
description = models.TextField(blank=True, null=True)
company = models.ForeignKey(Company, blank=True, null=True)
To this:
class DistList(models.Model):
creator = models.ForeignKey(User, related_name='creator')
created_date = models.DateTimeField(auto_now_add=True)
name = models.CharField(max_length=200, unique=True)
description = models.TextField(blank=True, null=True)
company = models.ForeignKey(Company)
The only change was turning the company FK relationship from not required to required.
When I run the migration I specify a one off value that corresponds to the pk of the first company.
./manage.py schemamigration distlist --auto
? The field 'DistList.company' does not have a default specified, yet is NOT NULL.
? Since you are making this field non-nullable, 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
? Please select a choice: 2
? Please enter Python code for your one-off default value.
? The datetime module is available, so you can do e.g. datetime.date.today()
>>> 1
But when I run the migration I get an error because it has a pending trigger event?
./manage.py migrate distlist
Running migrations for distlist:
- Migrating forwards to 0005_auto__chg_field_distlist_company.
> distlist:0005_auto__chg_field_distlist_company
FATAL ERROR - The following SQL query failed: ALTER TABLE "distlist_distlist" ALTER COLUMN "company_id" SET NOT NULL;
The error was: cannot ALTER TABLE "distlist_distlist" because it has pending trigger events
I'm not doing anything that seems weird from my point of view so I don't understand this error at all. Can anyone offer insight? I can post the full stack trace if it'll help but I feel like theres something obvious about south and postgresql that perhaps I'm missing?
So I believe I've found the answer. I think postgresql doesn't like altering schemas and adding data at the same time. I first created a datamigration:
./manage.py datamigration distlist add_default_values_to_existing_companies
Then I added this to the forwards method:
def forwards(self, orm):
"Write your forwards methods here."
for distlist in orm['distlist.Distlist'].objects.all():
distlist.company = orm['userprofile.Company'].objects.get(id=1)
distlist.save()
Then I altered the model to remove the blank and null from company.
Then I ran the schema migration and chose to specify a one off for the value as 1 (as I did in the question).
Then I edited that migration file thusly:
def forwards(self, orm):
# Changing field 'DistList.company'
# db.alter_column(u'distlist_distlist', 'company_id', self.gf('django.db.models.fields.related.ForeignKey')(default=1, to=orm['userprofile.Company']))
db.alter_column(u'distlist_distlist', 'company_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['userprofile.Company']))
I just commented out the generated line and removed the default=1 arg.
I don't know... maybe this isn't right but it seemed to work. Hopefully this will help someone.

How to add ManyToManyField in Existing django model?

I have a follwing two tables:
class Visit(models.Models):
date_created = models.DateTimeField(auto_now_add=True)
date_modified = models.DateTimeField(auto_now=True)
date_started = models.DateTimeField(null=True, blank=True)
date_completed = models.DateTimeField(null=True, blank=True)
# Here i want to add ManyToManyField
research = ManyToManyField(ResearchProtocol) #Here i will write for adding the field
class ResearchProtocol(models.Model):
title = models.CharField(max_length=30)
description = models.TextField()
start_date = models.DateField()
end_date = models.DateField()
def __unicode__(self):
return '%s' % self.title
For that i have written sql query :
CREATE TABLE "visit_visit_research" (
"id" serial NOT NULL PRIMARY KEY,
"visit_id" integer NOT NULL REFERENCES "visit_visit" ("id") DEFERRABLE INITIALLY DEFERRED,
"research_id" integer NOT NULL REFERENCES "www_researchprotocol" ("id") DEFERRABLE INITIALLY DEFERRED,
UNIQUE ("visit_id", "research_id")
)
;
When i execute this file the field is created somehow but when i open vist admin
and click to a particular id that leads to change form it gives me the following error:
http://localhost:8000/admin/visit/visit/20/
Exception Type: DatabaseError at /admin/visit/visit/20/
Exception Value: column visit_visit_research.researchprotocol_id does not exist
LINE 1: ...visit_research" ON ("www_researchprotocol"."id" = "visit_vis...
^
Somebody said that you need south and it cannot be done without south. Is that the only solution ? I am using Django 1.3.1, Python 2.7.2.
Can somebody guide me what mistake i am doing?
Any help will be appreciated.
Thanks in advance.
You've called your linking table visit_visit_research, and the field within it research_id, whereas Django is expecting visit_visit_researchprotocol and researchprotocol_id respectively.
Whenever I have modified the table models I used south and these commands to modify the structure and they always worked:
python manage.py convert_to_south "your_app"
python manage.py migrate "your_app"
You could try these and it should work, if you still have south installed.