Django: New Field Addition Gives Error while doing makemigration - django

I have a Post models for blog:
сlass Post(models.Model):
author = models.ForeignKey(User,
related_name="blog_posts",
on_delete=models.CASCADE)
........
........
And I want to add a Tag to the project, so I made a Tag model:
class Tag(models.Model):
tag_name = models.CharField(max_length=20,
blank=True)
def __str__(self):
return self.tag_name
I added this to the Post model:
tag = models.ForeignKey(Tag, related_name="blog_tag",
on_delete=models.CASCADE)
But makemigration give to me an error:
You are trying to add a non-nullable field 'tag' to post 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
So why i have to populate existing rows? And how i can to live the rows empty?
P.S. If i chose 1) i still have an error.

You already have data in your db and now you are adding a new column tag in your database table, which is also not null.
So django is saying that either you provide one-default data for the previously existing rows or you make this new column (tag) a null value (by adding null=True in arguments of the field) so that already existing rows will fill it with null.

Related

Django admin: Editing records with unique fields fails

Python 3.9, Django 3.2, Database is PostgreSQL hosted on ElephantSQL.
I have a model with a slug field which I have set to unique:
class website_category(models.Model):
fld1 = models.CharField(primary_key=True, max_length=8)
fld2 = models.TextField()
fld3 = models.SlugField(unique=True, db_index=True, max_length=100)
I can create new records for this model without any issue. However, when I try to edit an already existing record via the Django admin interface (e.g., change the text field fld2), Django throws this error:
website_category with this fld3 already exists
I can delete said record and re-enter the modified one without any issues, and I can edit the record if I change the slug field but not otherwise.
My guess is this is happening due to the "unique=True" parameter set in the slug field (fld3). However, I do want the slugs to be unique.
Is this an expected behavior of Django, or can I do something to make it possible for me to edit the records directly without having to delete and recreate them?
====
Edit: Additional Info
the model does not have any custom save method or ModelAdmin class. It is registered simply via admin.site.register(). The model does have a meta class which is being used to define some DB level constraints:
class Meta:
constraints = [
models.CheckConstraint(check=models.Q(fld1__iregex = r'^\w{8}$'),
name="%(app_label)s_%(class)s_id_validator"),
models.CheckConstraint(check=models.Q(fld3__iregex = r'^[\w-]+$'),
name="%(app_label)s_%(class)s_slug_validator"),
# DB level condition to enforce slug format (word characters and '-')
]

Can't add to ManyToManyField in Django custom task

I have two models in my Django app (Tag and MyModel).
MyModel has a ManyToManyField (tags) that uses the Tag model
class Tag(models.Model):
CATEGORY_CHOICES = (
('topic', 'topic')
)
tag = models.CharField(max_length=100, unique=True)
category = models.CharField(max_length=100, choices=CATEGORY_CHOICES)
class MyModel(models.Model):
id = models.CharField(max_length=30, primary_key=True)
title = models.CharField(max_length=300, default='')
author = models.CharField(max_length=300)
copy = models.TextField(blank=True, default='')
tags = models.ManyToManyField(Tag)
I have a custom Django management task where I delete the Tags table, get new data, and refresh the Tags table with bulk_create
Tag.objects.all().delete()
....get new data for Tag table
Tag.objects.bulk_create(new_tags)
However after this - if I try to add a tag to an instance of MyModel.tags...
mymodel_queryset = MyModel.objects.all()
for mymodel in mymodel_queryset
mymodel.tags.add(5)
...I get this error:
CommandError: insert or update on table "bytes_mymodel_tags" violates foreign key constraint ....
DETAIL: Key (tag_id)=(5) is not present in table "bytes_tag".
It seems like the Tag table is empty even though I just updated it
For some reason deleting and resetting the Tag table prevents me from adding to an instance of MyModel.tags. How can I do this?
Note: If I don't first delete and reset the Tags table then I can add to MyModel.tags just fine
Instead of adding random number in mymodel.tags.add you have to add Tag object.
mymodel_queryset = MyModel.objects.all()
for mymodel in mymodel_queryset
mymodel.tags.add(new_tags)
Im expanding a little bit on what has already been said here above:
When you have a m2m relationship, if you want to create a new row in the through table by using add you have to do so by adding an instance of the corresponding class connected through the relationship.
So if you have Tag and MyModel and you want to add a Tag object to MyModel, you have to first create a MyModel instance and then add to it a Tag instance.
For example:
tag_1 = Tag.objects.create(tag="test", category="test")
my_model_1 = MyModel.objects.create(title="my Title", author="me", copy="xxx")
# This is how you do it
my_model_1.add(tag_1)
Et voilà!
Note that when you use .add(instance) the save() method is built-in so you don't need to call it.

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

How to resolve this, without providing any default value?

When I am trying to run python3 manage.py makemigrations it shows :
You are trying to add a non-nullable field 'topic_id' to journey_template 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
Select an option:
enter code here
from django.db import models
class Topic_Table(models.Model):
topic_id=models.IntegerField(primary_key=True)
topic_name = models.CharField(max_length=100, blank=True)
weightage = models.CharField(max_length=15, blank=True)
grade = models.IntegerField( null=True)
subject = models.CharField(max_length=100, blank=True)
sequence = models.IntegerField(null=True)
month = models.CharField(max_length=15, blank=True)
class Journey_template(models.Model):
student_id = models.IntegerField(default=1)
topic_id = models.ForeignKey('Topic_Table',on_delete=models.CASCADE)
subtopic_id = models.IntegerField()
journey_template_key = models.IntegerField(primary_key=True)
How would I fix this?
You are adding the topic_id field on Journey_template model.
This model already has data on your database.
You have a few options:
1 - provide a default value (like entering the number 1)
2 - delete your database and start with a fresh migration
If you table already has data, adding a non-nullable column without any default will violate the non-null constraint. Django migration doesn't check the table for data, so it assumes there is some.
If your table has no rows, you can go ahead and give it any legal default value. The default does not change your model, it just gives Django a value it can fill in. Since there are no rows of data in the table, it will have no effect other than to make Django happy.
If you table has rows, then if you can think of a sensible value to populate the existing rows with, then use it. Your other option is to change the model by adding null=True to the field and Django will just put nulls in that field for existing rows. (Later, you can put your own values in to those fields with Django or other methods and change the field back to null=False if you like. You will get the same question when you migrate but the answer will have no effect if the fields are not null). null=False is the default for any field in your model.

Django queryset ProgrammingError: column does not exist

I'm facing a big issue with django.
I'm trying to save object containing foreignKeys and 'ManyToMany` but i always get this error
ProgrammingError: column [columnName] does not exist
I've made serveral times all migrations but it doesn't works. I have no problem when i work with models that does not contain foreign keys. I have tried to delete the migration folder. It's seems my database doesn't want to update fields. I need to force it to create these column but i don't have any idea.
class Post(models.Model):
post_id = models.CharField(max_length=100,default="")
title = models.CharField(max_length=100,default="")
content = models.TextField(default="")
author = models.ForeignKey(Users, default=None, on_delete=models.CASCADE)
comments = models.ManyToManyField(Replies)
numberComments = models.IntegerField(default=0)
date = models.DateTimeField(default=timezone.now)
updated = models.DateTimeField(null=True)
def __str__(self):
return self.post_id
when i'm trying to retrieve this i have :
ProgrammingError: column numberComments does not exist
As i said before i made makemigrations and migrate, i even deleted the migration folder.
Any idea ?
To save a instance of the POST model with foreign key you need to insert the query object.
Code example:
user = Users.objects.get(pk = 1)
p = POST(
title = 'Hello',
...
author = user,
date = '2018-01-01'
)
p.save()
You don't need to create post_id column, django creates one for you automatically, and you can access that using .pk or .id
You neither need numberComments. You should calculate that from comments many to many relation. Well... you can have this on DB too.
Next, you cannot add a many to many relation on creation. Create the post first as above. Then query the comment you want to add, the add the object to the relation
r = Replies.objects.get(pk = 1)
p.comments.add(r)
Hope it helps