Can't syncdb with UUID field - python

I'm trying to generate a new database with uuid using django and postgresql
I have a model with UUID field like this :
class BaseUser(AbstractBaseUser):
id = models.UUIDField(_('id'), primary_key=True, default=uuid.uuid4, editable=False)
When i try to syncdb i got the following error :
django.db.utils.ProgrammingError: column "id" cannot be cast automatically to type uuid
HINT: Specify a USING expression to perform the conversion.
I already try to modify the type by hand doing:
CREATE EXTENSION "uuid-ossp";
ALTER TABLE baseuser ALTER COLUMN id SET DATA TYPE UUID USING (uuid_generate_v4());
end up with this error:
ERROR: default for column "Id" cannot be cast automatically to type uuid
I also tried sqlite without success
I just want to generate a new db with uuid, I don't care about existing data.

Related

Why does a SQLite database created in Python have the VARCHAR data type?

When I create an SQLite database from a python data model, any column defined as a String in Python is displayed as VARCHAR in SQLite (viewing with DB Browser for SQLite). Here is an example of the data model in Python:
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
role = db.Column(db.String(10))
name_first = db.Column(db.String(50), nullable=False)
name_last = db.Column(db.String(50), nullable=False)
This may not be relevant, but I should clarify that I'm doing this as part of a website hosted with Flask. The database is initially created by dropping to a python prompt and:
from app import db
db.create_all()
I have a basic understanding of MS SQL and SQLite datatypes (NULL,INTEGER,REAL,TEXT,BLOB), but I don't understand why I'm seeing the columns defined as Strings in Python classified as VARCHAR in DB Browser for SQLite. If I attempt to modify the table, I see all of the expected datatypes for SQLite and also VARCHAR as an option. If I create a new database/table, then VARCHAR doesn't exist as an option for datatypes. Why wouldn't these columns be displayed as TEXT datatypes?
Strings in Python classified as VARCHAR in DB Browser for SQLite.
In Flask you are actually using SQLAlchemy ORM which will convert your class models directly into SQLite statements to create the relational database tables corresponding to it.
String data type in Python class model will be as VARCHAR data type in SQLite using Object Relational Mapper.
In SQLite, Text is the parent of VARCHAR and in the default installation is no different, so VARCHAR is actually same as TEXT.
Also If you check 3.1. Determination Of Column Affinity in the documentation you notice in the second point that:
If the declared type of the column contains any of the strings "CHAR",
"CLOB", or "TEXT" then that column has TEXT affinity. Notice that the
type VARCHAR contains the string "CHAR" and is thus assigned TEXT
affinity.
For more info check : http://www.sqlite.org/datatype3.html

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

Django vs MySQL uuid

Django noob here
I have created a model using
customer_id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
After migrating the model to MySQL, I tried to add data into mysql using
insert into customer_customer (customer_id, ...) values (uuid(), ...)
The data gets inserted properly in MySQL with a unique code, however, when I try to display this via Django admin tool (this table feeds into a property for users), it throws a badly formatted uuid error.
File "/usr/lib/python2.7/uuid.py", line 134, in __init__
raise ValueError('badly formed hexadecimal UUID string')
ValueError: badly formed hexadecimal UUID string
Please discuss if there is another way of creating seed data directly in MySQL.
A field for storing universally unique identifiers. Uses Python’s UUID
class. When used on PostgreSQL, this stores in a uuid datatype,
otherwise in a char(32).
So with MySQL django handles the uuid, and manages the field as Char32. You can't use native MySQL uuid.
If you have to create uuid from the MySQL side, use a CharField in django model, and populate it:
class MyModel(models.Model):
fld = models.CharField(max_length=36)
Then when saving:
import uuid
MyModel.fld = str(uuid.uuid4())
As a default:
fld = models.CharField(max_length=36, default=uuid.uuid4)
Try this:
insert into customer_customer (customer_id, ...) values (Replace(uuid(),'-',''), ...)
then it will work.
Documentation states that if you use a MySQL database, Django will store a string (char32):
UUIDField.
A field for storing universally unique identifiers. Uses Python’s UUID class. When used on PostgreSQL, this stores in a uuid datatype, otherwise in a char(32).
Python's uuid module gives you the following options to generate UUIDs:
>>> import uuid
>>> uuid.uuid4()
UUID('bd65600d-8669-4903-8a14-af88203add38')
>>> str(uuid.uuid4())
'f50ec0b7-f960-400d-91f0-c42a6d44e3d0'
>>> uuid.uuid4().hex
'9fe2c4e93f654fdbb24c02b15259716c'
In your case (using uuid4 as default in the Django module), you will need to use the "UUID.uuid4().hex" option in order to save the UUID as a string, just like Django would save it in your MySql database.

Categories

Resources