Rename a field which was Primary Key in models.py - django

I have a model with the following fields:
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
department = models.TextField(verbose_name="department")
username = models.CharField(max_length=30, unique=True)
emp_id = models.AutoField(primary_key=True)
is_admin = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
hide_email = models.BooleanField(default=True)
name = models.TextField(verbose_name="employee name")
I want to rename "emp_id" field to "id", as you can see it is the primary key field. Can you please create a migration file for the same?
Thank you

You modify the field to:
class MyModel(models.Model):
# …,
id = models.AutoField(primary_key=True, db_column='emp_id')
# …
If you then run manage.py makemigrations, Django will ask if you renamed the fied:
Did you rename mymodel.emp_id to mymodel.id (a AutoField)? [y/N]
a question you answer with yes.
This will still use the emp_id as the database id. If you want to rename the database column as well, you can just remove the emp_id field, and Django will use the default id field as primary key instead, and you can let Django make migrations the same way.

from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('your_app_name', 'name_of_previousmigration_file'),
]
operations = [
migrations.RemoveField(
model_name='modelname',
name='emp_id',
)
]

class ModelName(models.Model):
id = models.AutoField(primary_key=True)
# other fields
Then run python manage.py makemigrations
A message will appear which will tell you to confirm that you want to rename the field emp_id to id. Type y to confirm yes.
Then run python manage.py migrate
In you application directory you'll found migration files.

Related

Django ID of foreign key doesn't exist after migrating

I'm new to Django, and I'm trying to create a "game" model with two attributes:
A many-to-one field where multiple instances of the game model are associated with an instance of a custom user model.
A many-to-many field where instances of the game model are connected with multiple instances of words, and instances of the word model are connected with multiple instances of the game model
Top of my models.py model:
from django.db import models
from users.models import CustomUser
from django.contrib.postgres.fields import ArrayField
Game model:
class SortingGame(models.Model):
user_current_player = models.ForeignKey(CustomUser, on_delete=models.CASCADE, null=True, blank=True)
field_words = models.ManyToManyField(Word, related_name="field_sorting_games")
Word model:
class Word(models.Model):
str_word = models.CharField(max_length=50,null=True)
int_grade_level = models.IntegerField()
arrint_phonemes = ArrayField(models.CharField(max_length=50),null=True)
arrstr_graphemes = ArrayField(models.CharField(max_length=50),null=True)
int_num_syllables = models.IntegerField()
arrstr_syllables = ArrayField(models.CharField(max_length=50),null=True)
User model:
class CustomUser(AbstractBaseUser):
# must have the following fields for django
email = models.EmailField(verbose_name="email",max_length = 100,unique=True)
username = models.CharField(max_length = 30, unique = True)
date_joined = models.DateTimeField(verbose_name = "date_joined",auto_now_add=True)
last_login = models.DateTimeField(verbose_name = "last_login",auto_now = True)
is_admin = models.BooleanField(default=False)
is_superuser = models.BooleanField(default = False)
is_staff = models.BooleanField(default = False)
is_active = models.BooleanField(default = True)
first_name = models.CharField(max_length=15, blank=True)
last_name = models.CharField(max_length=30, blank=True)
spelling_level = models.IntegerField(default=1, unique=False)
time_played = models.IntegerField(default=0, unique=False)
percent_correct = models.IntegerField(default=0, unique=False)
admin.py:
from django.contrib import admin
from .models import Word, SortingGame
admin.site.register(SortingGame)
When I run python3 manage.py makemigrations and python3 manage.py migrate, it doesn't complain, but when I go to the admin page of my django site it says psycopg2.errors.UndefinedColumn: column "user_current_player_id" of relation "game_sortinggame" does not exist.
This makes me think the issue is with user_current_player in SortingGame (it worked fine before I added that attribute), but I've looked around on different forums to see what might be going wrong and I can't seem to figure it out. I tried starting from scratch with a new database, and it's still throwing the same exception. Any ideas would be appreciated—thanks!
Nathan!
First thing would be make sure that you have the app where CustomUser model is created in your settings.py file, at INSTALLED_APPS.
If so, please have a look at this folder (app) where you have CustomUser defined to verify if there is in deed a migrations folder there.
I suspect that Django in not aware of this app (not under INSTALLED_APPS) and therefore did not migrated it. So, your database is not finding the User Model connection.
That said, I would suggested you to keep your account model as defined by Django User and create another model with a direct relationship to it to deal with profile/game fields such as spelling level, percentage_correct and so on.
This would keep your Model "concerns" more organized later on.
if you did make a migrations before try to use (python manage.py makemigrations -appname)
Also after That you need to Add the module in your admin.py
from django.contrib import admin
from .models import *
admin.site.register(SortingGame)
... all other modules

You are trying to change the nullable field 'email' on customuser to non-nullable without a default

I have two models (UserAddress and CustomUser) in my models.py, the field user address in CustomUser was a many to many field but I decided to change it to a foreign key field. But when I ran python manage.py make migrations it asked me to choose a choice:
what should I do:
You are trying to change the nullable field 'email' on customuser to non-nullable without a default; we can't do that (the database needs something to populate exis
ting rows).
Please select a fix:
Provide a one-off default now (will be set on all existing rows with a null value for this column)
Ignore for now, and let me handle existing rows with NULL myself (e.g. because you added a RunPython or RunSQL operation to handle NULL values in a previous dat
a migration)
Quit, and let me add a default in models.py
Here is my models.py file:
class UserAddress(models.Model):
city = models.CharField(max_length=100)
address = models.CharField(max_length=200)
zip_code = models.CharField(max_length=15, blank=True)
def __str__(self):
return str(self.id)
class CustomUser(AbstractUser):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField(unique=True)
user_address = models.ForeignKey(UserAddress, on_delete=models.CASCADE)
class CustomUser(AbstractUser):
email = models.EmailField(unique=True ,null =True)
# your other fields
Run python manage.py makemigrations and python manage.py migrate command. The error will be removed.

Why does Django with sqlite not insert unique constraint into the database schema?

I am starting to use Django with sqlite and I want to impose a simple unique constraint on the combination of two fields in a table. To this end, I use Django's UniqueConstraint class. However, to my surprise, the constraint is not inserted in to the tables schema in the database. My Django model looks like follows:
from django.db import models
class Fruit(models.Model):
fruit_id = models.IntegerField(primary_key=True)
fruit_type = models.CharField(max_length=64, default='', blank=True, null=False)
fruit_name = models.CharField(max_length=128, default='', blank=True, null=False)
class Meta:
models.UniqueConstraint(fields=['fruit_type', 'fruit_name'], name='unique_fruit_type_name')
db_table = 'fruit'
After migration, I check the schema in the database by executing:
select sql from sqlite_master where type='table';
The result, for the table in question, reads:
CREATE TABLE "fruit" ("fruit_id" integer NOT NULL PRIMARY KEY"fruit_type" varchar(64) NOT NULL), "fruit_name" varchar(128) NOT NULL
I was expecting to get:
CREATE TABLE "fruit" ("fruit_id" integer NOT NULL PRIMARY KEY"fruit_type" varchar(64) NOT NULL), "fruit_name" varchar(128) NOT NULL, UNIQUE (fruit_type, fruit_name)
Where did the UNIQUE-clause go? Is it not supposed to be in the schema?
You write the constraint directly in the Meta class which has no effect. You instead need to write it in a list on the attribute constraints [Django docs] in the Meta class:
from django.db import models
class Fruit(models.Model):
fruit_id = models.IntegerField(primary_key=True)
fruit_type = models.CharField(max_length=64, default='', blank=True, null=False)
fruit_name = models.CharField(max_length=128, default='', blank=True, null=False)
class Meta:
constraints = [
models.UniqueConstraint(fields=['fruit_type', 'fruit_name'], name='unique_fruit_type_name')
]
db_table = 'fruit'
Note: Make sure to run python manage.py makemigrations and python manage.py migrate after making these changes.

How can I set/provide a default value while django migration?

Scenario:
I have a model, Customer
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
and now I updated the company attribute witha ForeignKey relationship as below,
class Company(models.Model):
name = models.CharField(max_length=100)
location = models.CharField(max_length=100)
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.ForeignKey(Company)
What I need is, when the new migrations applied to the DB,corresponding Company instance must automatically generate and map to the company attribute of Customer instance.Is that possible? How can I achieve this ?
Let's start from your original model and do it step by step.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
First you would have to keep the original field and create a new one, to be able to restore the old data afterwards.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.CharField(max_length=100)
_company = models.ForeignKey(Company)
Now you can create a first migration with manage.py makemigrations. Then you will have to create a data migration. Create the migration using manage.py makemigrations yourapp --empty and update the generated file:
from django.db import migrations
def export_customer_company(apps, schema_editor):
Customer = apps.get_model('yourapp', 'Customer')
Company = apps.get_model('yourapp', 'Company')
for customer in Customer.objects.all():
customer._company = Company.objects.get_or_create(name=customer.company)[0]
customer.save()
def revert_export_customer_company(apps, schema_editor):
Customer = apps.get_model('yourapp', 'Customer')
Company = apps.get_model('yourapp', 'Company')
for customer in Customer.objects.filter(_company__isnull=False):
customer.company = customer._company.name
customer.save()
class Migration(migrations.Migration):
dependencies = [
('yourapp', 'xxxx_previous_migration'), # Note this is auto-generated by django
]
operations = [
migrations.RunPython(export_customer_company, revert_export_customer_company),
]
The above migration will populate your Company model and Customer._company field according to Customer.company.
Now you can drop the old Customer.company and rename Customer._company.
class Customer(models.Model):
name = models.CharField(max_length=100)
age = models.IntegerField()
company = models.ForeignKey(Company)
Final manage.py makemigrations and manage.py migrate.
Sure, but you have to do three migrations and the fields cant be named the same thing as both need to exist at the same time. If you already have removed the company field in your real database you are SOL and will have to fix them manually.
First, add the Company model in a normal db migration, then do a data migration and have it run after the first db migration, then do another db migration removing the company field from the Customer model.
The db migrations you can do with manage.py makemigrations as usual, just add something like below in a migration file between them, here i named the new company ForeignKey field to company_obj
def fix_companies(apps, schema_editor):
Company = apps.get_model("myapp", "Company")
Customer = apps.get_model("myapp", "Customer")
for c in Customer.objects.all():
company, _ = Company.objects.get_or_create(name=c.name)
c.company_obj = company
c.save()
def rev(apps, schema_editor):
# the reverse goes here if you want to copy company names into customer again if you migrate backwards.
pass
class Migration(migrations.Migration):
dependencies = [
('myapp', 'XXXX_migration_that_added_company_model'),
]
operations = [
migrations.RunPython(fix_companies, rev),
]
Something to note is if you are going through a cycle of renaming/ deprecating fields, using RunPython would leave you pointing to old model fields that wouldn't exist anymore after you are done with your field changes.
To avoid this, you might want to go with RunSQL instead.
# Generated by Django 3.2.3 on 2022-02-09 04:55
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("<your_app>", "<0006_migration_name>"),
]
operations = [
migrations.RunSQL(f"""
update public.<table_name> set new_field = old_field + some_magic;
"""
),
]
Docs.

Multiple primary keys for table "app_employee" are not allowed.

Django 1.11 with PostgreSQL.
I go to migrate my site and models.py throws the error that I can't have more than one primary key. I can't see where I do (or I'm not understanding how).
class Employee(models.Model):
Aegis_ID = models.UUIDField(primary_key=True, null=False, default=uuid.uuid4, editable=False, serialize=True)
Employee_Number = models.ForeignKey('self', on_delete=models.CASCADE, related_name='Company_Employee_Number',
null=True, blank=True, max_length=6, help_text="Employee ID")
Employee_FName = models.CharField(null=True, blank=True, max_length=25, help_text="First Name")
Employee_LName = models.CharField(null=True, blank=True, max_length=25, help_text="Last Name")
Employee_Email = models.EmailField(max_length=80, blank=True, help_text="GPM Email address")
Employee_Position = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True,
related_name='Department_Employee_Position', max_length=3,
choices=EMPLOYEE_POSITION, help_text="Select position of this Employee.")
Hire_Date = models.DateField(null=True, blank=True, help_text="Enter the employee hire date.")
Employee_Division = models.CharField(max_length=2, null=True, blank=True, choices=DIVISION_CHOICES,
help_text="Assign the Audit Division for this employee.")
Employee_Region = models.CharField(max_length=3, null=True, blank=True, choices=REGION_CHOICES,
help_text="Assign the Audit Region for this employee.")
Employee_District = models.CharField(max_length=3, null=True, blank=True, choices=DISTRICT_CHOICES,
help_text="Assign the Audit District for this Employee.")
Reading the Django pages on this exact topic, it's listed as a problem resolved in 1.7 and had to do with how Django sorted the tables by class, alphabetically.
I've also tried python manage.py flush followed by makemigrations prior to migrate
So, what fields is Django / Postgres attempting to make an "id" and "primary" because I'm just not understanding, here...
According to the Django documentation regarding Automatic Primary Keys, there's the unseen is id = models.AutoField(primary_key=True) but I also understood that if you assign the primary_key=True to a field, this did not apply
In your above model Multiple primary keys for table “app_employee” are not allowed.
It is not coming because you have
Aegis_ID = models.UUIDField(primary_key=True, null=False, default=uuid.uuid4, editable=False, serialize=True)
Because in django documentation it is clearly specified that
Django Documentation
Field.primary_key
If True, this field is the primary key for the model.
If you don’t specify primary_key=True for any field in your model, Django will automatically add an AutoField to hold the primary key, so you don’t need to set primary_key=True on any of your fields unless you want to override the default primary-key behaviour.
primary_key=True implies null=False and unique=True. Only one primary key is allowed on an object.
I have tried your model on my project and it is working absolutely fine.
For simplicity I removed other fields
models.py
from __future__ import unicode_literals
from django.db import models
import uuid
class Employee(models.Model):
Aegis_ID = models.UUIDField(primary_key=True, null=False,default=uuid.uuid4, editable=False, serialize=True)
Employee_Number = models.ForeignKey('self', on_delete=models.CASCADE, related_name='Company_Employee_Number',
null=True, blank=True, max_length=6, help_text="Employee ID")
Employee_FName = models.CharField(null=True, blank=True, max_length=25, help_text="First Name")
Employee_LName = models.CharField(null=True, blank=True, max_length=25, help_text="Last Name")
Employee_Email = models.EmailField(max_length=80, blank=True, help_text="GPM Email address")
and when I did
(venv) astikanand#Developer-PC:~/firstsite$ python manage.py makemigrations
Migrations for 'employee':
employee/migrations/0001_initial.py
- Create model Employee
and then
(venv) astikanand#Developer-PC:~/firstsite$ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, employee, sessions
Running migrations:
Applying employee.0001_initial... OK
so it is working fine.
You need to do is
Either you recreate your app or simply start your project all over again, may be some dependency issues or something. But your code for model Employee is all ok.
It is quiet possible that you changed primary keys and/or references to other models/tables and some legacy dependencies remained in the migration files.
Please refer to the official Django documentation for reverting past migrations. You do not have to restart your project to remove dependencies.
python manage.py makemigrations --empty yourappname
That is it. Then check 0001_initial.py file under migrations folder in your app to make sure all dependencies have been removed. It should look like:
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('yourappname', '0001_initial'),
]
operations = [
]