How to add ManyToManyField in Existing django model? - django

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.

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'

Integrity Error Not Null Constraint Failed When Attempting to Migrate

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].

Making use of the users table, causing an error

In Django (2.x) I have an entry form, the model is here:
from django.db import models
from django.contrib.auth.models import User
from django.conf import settings
class Sample(models.Model):
sample_id = models.AutoField(primary_key=True)
area_easting = models.IntegerField()
area_northing = models.IntegerField()
context_number = models.IntegerField()
sample_number = models.IntegerField()
# taken_by = models.IntegerField()
taken_by = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete = models.PROTECT)
def __str__(self):
return str(self.sample_id)
class Meta:
db_table = 'samples\".\"sample'
#ordering = ["sample_id"]
managed = False
#verbose_name_plural = "samples"
This works as expected, a list of usernames drops down (while I would like to format - firstname lastname). However, when I return to the main viewing page I see an error.
django.db.utils.ProgrammingError: column sample.taken_by_id does not exist
LINE 1: ...text_number", "samples"."sample"."sample_number", "samples"....
^
HINT: Perhaps you meant to reference the column "sample.taken_by".
Clearly Django is adding the _id to the table name causing the error, I expect because it is a foreign key.
Any ideas how to remedy this behaviour?
You can explicitly set the underlying db column via the db_column attribute:
taken_by = models.ForeignKey(settings.AUTH_USER_MODEL, db_column='taken_by', on_delete=models.PROTECT)
https://docs.djangoproject.com/en/2.1/ref/models/fields/#database-representation
^ link to the docs where it specifies that it creates a _id field.
based from the error message you have posted. It seems that your database schema is not updated.
you might need to do manage makemigrations and migrate to apply your model changes to your db schema
e.g
$ python manage.py makemigrations
# to apply the new migrations file
$ python manage.py migrate

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.