I seem to be stuck with creating an initialy deferrable foreign key relationship between two models in Django and using SQLite3 as my backend storage.
Consider this simple example. This is what models.py looks like:
from django.db import models
class Investigator(models.Model):
name = models.CharField(max_length=250)
email = models.CharField(max_length=250)
class Project(models.Model):
name = models.CharField(max_length=250)
investigator = models.ForeignKey(Investigator)
And this is what the output from sqlall looks like:
BEGIN;
CREATE TABLE "moo_investigator" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(250) NOT NULL,
"email" varchar(250) NOT NULL
)
;
CREATE TABLE "moo_project" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(250) NOT NULL,
"investigator_id" integer NOT NULL REFERENCES "moo_investigator" ("id")
)
;
CREATE INDEX "moo_project_a7e50be7" ON "moo_project" ("investigator_id");
COMMIT;
"DEFERRABLE INITIALLY DEFERRED" is missing from the *investigator_id* column in the project table. What am I doing wrong?
p.s. I am new to Python and Django - using Python version 2.6.1 Django version 1.4 and SQLite version 3.6.12
This behavior is now the default. See https://github.com/django/django/blob/803840abf7dcb6ac190f021a971f1e3dc8f6792a/django/db/backends/sqlite3/schema.py#L16
Sqlite backend does not add "DEFERRABLE INITIALLY DEFERRED". Check the code
Related
I'm trying to create models taking a MySQL database as reference using Django, but when I try to migrate, it throws me the following error:
Cannot change column 'id': used in a foreign key constraint 'fk_post' of table 'demoniosmv.images'"
My models look like this:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Images(models.Model):
#id = models.IntegerField(primary_key=True)
fk_post = models.ForeignKey('Post', related_name="images", blank=True, null=True, on_delete = models.CASCADE)
img_url = models.CharField(max_length=100, blank=True, null=True)
class Meta:
managed = True
class Post(models.Model):
#id = models.IntegerField(primary_key=True)
author = models.ForeignKey(User, related_name="posts", on_delete = models.CASCADE)
title = models.CharField(max_length=200, blank=True, null=True)
text = models.TextField(blank=True, null=True)
created_date = models.DateTimeField(default = timezone.now)
class Meta:
managed = True
And the SQL script from which I'm creating the database is this:
-- MySQL Script generated by MySQL Workbench
-- Sun Jul 21 14:14:44 2019
-- Model: New Model Version: 1.0
-- MySQL Workbench Forward Engineering
SET #OLD_UNIQUE_CHECKS=##UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET #OLD_FOREIGN_KEY_CHECKS=##FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET #OLD_SQL_MODE=##SQL_MODE, SQL_MODE='ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION';
-- -----------------------------------------------------
-- Schema demoniosmv
-- -----------------------------------------------------
-- -----------------------------------------------------
-- Schema demoniosmv
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `demoniosmv` DEFAULT CHARACTER SET utf8 ;
USE `demoniosmv` ;
-- -----------------------------------------------------
-- Table `demoniosmv`.`post`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `demoniosmv`.`post` (
`id` INT NOT NULL,
`title` VARCHAR(200) NULL,
`text` TEXT(1000) NULL,
`created_date` DATETIME NULL,
PRIMARY KEY (`id`))
ENGINE = InnoDB;
-- -----------------------------------------------------
-- Table `demoniosmv`.`images`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `demoniosmv`.`images` (
`id` INT NOT NULL,
`fk_post` INT NULL,
`img_url` VARCHAR(100) NULL,
PRIMARY KEY (`id`),
INDEX `fk_post_idx` (`fk_post` ASC) VISIBLE,
CONSTRAINT `fk_post`
FOREIGN KEY (`fk_post`)
REFERENCES `demoniosmv`.`post` (`id`)
ON DELETE CASCADE
ON UPDATE NO ACTION)
ENGINE = InnoDB;
SET SQL_MODE=#OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=#OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=#OLD_UNIQUE_CHECKS;
For creating the models I used the insepctdb command, I'm using sqlclient for the MySQL connection, and I'm using Django 2.2.3 with Python 3.7.
I had to delete the files inside migrations and py_cache folders to make it work. All the files except for the init.py files
I have the following table in mysql:
CREATE TABLE `portal_asset` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`asset_id` int(11) NOT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1000000 DEFAULT CHARSET=utf8;
How would I create thie same table in django? So far I have the following, but not sure how to set the AUTO_INCREMENT value --
class PortalAsset(models.Model):
id = models.IntegerField(primary_key=True)
asset_id = models.IntegerField(unique=True)
class Meta:
db_table = u'portal_asset'
How can I set the AUTO_INCREMENT value to start off at 1000000 ? The equivalent of:
alter table portal_asset AUTO_INCREMENT=1000000;
You can use a RunSQL operation in your migrations to execute the necessary SQL:
migrations.RunSQL("ALTER TABLE portal_asset AUTO_INCREMENT=1000000;")
If you haven't run any migrations, you can add this to your first migration to ensure no rows are inserted before the new value is set. Otherwise you'll have to add this operation in a new migration. You can create a new, empty migration using python manage.py makemigrations --empty <yourappname>.
While creating item I got this error, though I can see category_id at sqlall:
table items_item has no column named category_id
migration commands:
$ python manage.py makemigrations items
No changes detected
$ python manage.py migrate
Running migrations:
No migrations to apply.
model.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=200)
# Create your models here.
class Item(models.Model):
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True)
image = models.ImageField(upload_to="item_images")
category = models.ForeignKey(Category)
show_in_front_page= models.BooleanField(default=True)
always_show_in_front_page= models.BooleanField(default=True)
is_trending=models.BooleanField(default=True)
sqlall
# $ python manage.py sqlall items
BEGIN;
CREATE TABLE "items_category" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(200) NOT NULL
)
;
CREATE TABLE "items_item" (
"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT,
"name" varchar(200) NOT NULL,
"description" text,
"image" varchar(100) NOT NULL,
"category_id" integer NOT NULL REFERENCES "items_category" ("id"),
"show_in_front_page" bool NOT NULL,
"always_show_in_front_page" bool NOT NULL,
"is_trending" bool NOT NULL
)
;
CREATE INDEX "items_item_6f33f001" ON "items_item" ("category_id");
COMMIT;
admin.py
from django.contrib import admin
# Register your models here.
from .models import Item, Category
admin.site.register(Item)
admin.site.register(Category)
If the category_id column is already in an applied migration file, but hasn't been created in the database., then the easiest fix would be to add the column manually. It's easy to work out the SQL from your sqlall output above.
./manage.py dbshell # open a db shell
# Add the column
ALTER TABLE items_item ADD COLUMN "category_id" integer NOT NULL REFERENCES "items_category" ("id");
# Add the index
CREATE INDEX "items_item_6f33f001" ON "items_item" ("category_id");
try deleting your db.sqlite3 file.
Then run makemigrations and migrate (the sqlite file will generates automatically).
I am doing this tutorial tangowithdjango 5 Models and databases
In the exercises it asks to create a views and likes column with default=0 in the categories. Then to use population script to update them with values.
Direct quote from site
Update the Category model to include the additional attributes, views
and likes where the default value is zero.
So I have gone and created the categories in models.py
from django.db import models
from django.template.defaultfilters import default
# Create your models here.
class Category(models.Model):
name = models.CharField(max_length=128, unique=True)
views = models.IntegerField(default=0)
likes = models.IntegerField(default=0)
class Meta:
verbose_name_plural = "Categories"
def __unicode__(self):
return self.name
class Page(models.Model):
category = models.ForeignKey(Category)
title = models.CharField(max_length=128)
url = models.URLField()
views = models.IntegerField(default=0)
Then I sync.db
However the columns aren't creating I get this error from the view.
Exception Type: DatabaseError at /admin/rango/category/
Exception Value: no such column: rango_category.views
What is wrong with how I have created them? Do I have to remove all my database tables and recreate them?
Edit: The solution to Incorrect Duplicate doesn't resolve the issue, as well as my answer I thought would. Deleting the database and re-syncing didn't resolve the issue with creating the population script.
The sql appears to be ok I am unsure what is wrong.
BEGIN;
CREATE TABLE "rango_category" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(128) NOT NULL UNIQUE,
"views" integer NOT NULL,
"likes" integer NOT NULL
)
;
CREATE TABLE "rango_page" (
"id" integer NOT NULL PRIMARY KEY,
"category_id" integer NOT NULL REFERENCES "rango_category" ("id"),
"title" varchar(128) NOT NULL,
"url" varchar(200) NOT NULL,
"views" integer NOT NULL
)
;
COMMIT;
Finished "/home/sayth/workspace/tango_project/manage.py sql rango" execution.
Even re deleting database and redesigning and recreating models doesn't reolved.
BEGIN;
CREATE TABLE "rango_category" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(128) NOT NULL UNIQUE
)
;
CREATE TABLE "rango_page" (
"id" integer NOT NULL PRIMARY KEY,
"category_id" integer NOT NULL REFERENCES "rango_category" ("id"),
"title" varchar(128) NOT NULL,
"url" varchar(200) NOT NULL,
"views" integer NOT NULL,
"likes" integer NOT NULL
)
;
COMMIT;
Finished "/home/sayth/workspace/tango_project/manage.py sql rango" execution.
Ah yes I overlooked a warning in the doc.
Warning Whenever you add to existing database models, you will have to
delete the database file and then re-sync the database by running
python manage.py syncb again. This is a drawback of Django 1.5.4, and
can be quite frustrating. If you however add a new model, you can
syncdb your database without having to delete and recreate it. You
must therefore bear this in mind when tweaking your database: new
models will be synchronised with syncdb - but changes to existing
models will not be. When adding a new model to your application’s
models.py file, you can simply run the following command to
synchronise the database with the command $ python manage.py syncdb.
When updating an existing model to your application’s models.py file,
you must perform the following steps. Delete the database. Recreate
the database with the command $ python manage.py syncdb. Populate the
new database with data. Deleting and recreating the database from
scratch is a frustrating process. A possible solution to this issue
could be to use a third party application like South to handle
database schema migrations (changes to your models). South is
currently in active development and is considered a standard solution
for Django schema migrations until this functionality becomes part of
the standard Django codebase. We don’t cover South here - but the
official South documentation provides a handy tutorial if you’re
interested. If you don’t want to use South, we discuss a technique in
Section 5.8 to speed up the updating process. You may have also
noticed that our Category model is currently lacking some fields that
we defined in Rango’s requirements. We will add these in later to
remind you of the updating process.
And it appears migrations in core will not be available until django 1.7.
I had incorrectly updated the populate script it should have looked like.
def add_page(cat, title, url, views=0):
p = Page.objects.get_or_create(category=cat, title=title, url=url, views=views)[0]
return p
def add_cat(name, views=0, likes=0):
c = Category.objects.get_or_create(name=name, views=views, likes=likes)[0]
return c
see
https://stackoverflow.com/a/19759473/461887
django migrations
I've written a couple of tests for really simple blog app, but the many to many relationship fails when I run the test: ./manage.py test myblog
DatabaseError: no such table: myblog_post_tag
Yet when I do ./manage.py sql myblog:
BEGIN;
CREATE TABLE "myblog_tag" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(50) NOT NULL
)
;
CREATE TABLE "myblog_post_tag" (
"id" integer NOT NULL PRIMARY KEY,
"post_id" integer NOT NULL,
"tag_id" integer NOT NULL REFERENCES "myblog_tag" ("id"),
UNIQUE ("post_id", "tag_id")
)
;
CREATE TABLE "myblog_post" (
"id" integer NOT NULL PRIMARY KEY,
"title" varchar(200) NOT NULL,
"pub_date" datetime NOT NULL,
"content" text NOT NULL
)
;
COMMIT;
It does create a table, yet it fails to do so while testing? Any help is appreciated.
Here's my test:
class TagModelTest(TestCase):
def test_create_tags_for_posts(self):
# tests tagging posts, postodd will have tags 1 & 3, posteven will be 2 & 4
postodd = Post(
title="testing odd tags",
pub_date=timezone.now(),
content='''hello everybody, we are testing some tagging
functionality here. This post should have odd tags.''',
)
posteven = Post(
title="test even tags",
pub_date=timezone.now(),
content ='''hello everybody, we are testing some tagging
functionality here. This post should have even tags.''',
)
#save them to db
postodd.save()
posteven.save()
# create the tags
tag1 = Tag(name="1")
tag2 = Tag(name="2")
tag3 = Tag(name="3")
tag4 = Tag(name="4")
# save all tags to db
tag1.save()
tag2.save()
tag3.save()
tag4.save()
# create the many2many relationship
postodd.tag.add(tag1)
And my models.py if needed:
from django.db import models
class Tag(models.Model):
name = models.CharField(max_length=50)
def __unicode__(self):
return self.name
class Post(models.Model):
tag = models.ManyToManyField(Tag)
title = models.CharField(max_length=200)
pub_date = models.DateTimeField(verbose_name="Date published")
content = models.TextField()
def __unicode__(self):
return self.title
./manage.py sql myblog does not execute the SQL, it just outputs what it would execute if you ran syncdb.
In this case, it seems the table is missing from your db.
If this was a result of a modification to an existing app; for example you just added a new field to your model; then running syncdb won't affect the changes to your database. syncdb doesn't do any destructive operations (like adding or dropping tables or columns).
In this case you can manually run the query to add the column; or drop and recreate your tables with syncdb.
Since this is a common problem most people use a data migration tool like south to handle these changes for you. South will manage these small changes intelligently.