I am beginner in Django and just started learning migrations. In a DB table, I am trying to remove a row that has a specific field value. When I create a custom file which uses Django settings, the function properly deletes the row. But when I use the snippet in another function in a custom migration file, the deletion doesn't take place.
It is possible that I am missing something elementary as a beginner.
So I am trying to delete all orders the status of which is cancelled. The function that works in a separate file is:
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "orm_skeleton.settings")
django.setup()
from main_app.models import Order
def delete_test_fnc():
all_orders = Order.objects.all()
for i in range(len(all_orders) - 1, -1, -1):
order = all_orders[i]
if order.status == "Cancelled":
order.delete()
delete_test_fnc()
The custom migration where delete doesnt work (other two modifications work without issues) is:
from datetime import timedelta
from django.db import migrations
CURRENT_ORDERS = []
def modify_delivery_warranty(apps, schema_editor):
global CURRENT_ORDERS
orders = apps.get_model("main_app", "Order")
all_orders = orders.objects.all()
CURRENT_ORDERS = all_orders
for i in range(len(all_orders) - 1, -1, -1):
order = all_orders[i]
if order.status == "Cancelled":
order.delete()
elif order.status == "Pending":
order.delivery = order.order_date + timedelta(days=3)
elif order.status == "Completed":
order.warranty = "24 months"
order.save()
def revert_modify_delivery_warranty(apps, schema_editor):
orders = apps.get_model("main_app", "Order")
orders.objects.all().delete()
for order in CURRENT_ORDERS:
new_order = orders.objects.create(
product_name=order.get("product_name"),
customer_name=order.get("customer_name"),
order_date=order.get("order_date"),
amount=order.get("amount"),
product_price=order.get("product_price"),
total_price=order.get("total_price"),
warranty=order.get("warranty"),
delivery=order.get("delivery")
)
new_order.save()
class Migration(migrations.Migration):
dependencies = [
('main_app', '0021_order'),`your text`
]
operations = [migrations.RunPython(
modify_delivery_warranty,
reverse_code=revert_modify_delivery_warranty
)
]
The problem with your code is that if the status is Cancelled
, you call order.delete()
which deletes it from the database. But the Python object itself is still in the memory in variable order
.. After all if statements, you call order.save()
which saves the object back to database. Which means your object is indeed deleted but it is saved again.
So, a way to fix it is by calling order.save()
only when needed:
for i in range(len(all_orders) - 1, -1, -1):
order = all_orders[i]
if order.status == "Cancelled":
order.delete()
if order.status == "Pending":
order.delivery = order.order_date + timedelta(days=3)
elif order.status == "Completed":
order.warranty = "24 months"
if order.status != "Cancelled":
order.save()
Another important note is that the code you implemented will not revert back the deleted objects because once you run python manage.py migrate
and it stops, all in-memory objects are deleted after the process exists. Which means when you try to revert the migration, the CURRENT_ORDERS
will be empty. For more about this: https://docs.djangoproject.com/en/4.2/topics/migrations/