After writing up a number of CLI scripts to update various model values in a Django app, it occurred to me that a generic script could be written that would work for all of my purposes. What follows is a simple catch-all script that can be used in any django app as is!
The script works by taking in the following values:
- Model name
- Field name
- New value type
- New value
- Comma separated list of IDs
To use this script, place it in the following dir in your project: <project>/<app>/management/commands/
.
The script as written depends on the rich library: pip install rich
.
Let's say I have a model defining a Person
that includes a deleted
field acting as a soft delete.
from django.db import models
class Person(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
created = models.DateTimeField(auto_now_add=True, null=False)
deleted = models.BooleanField(default=False)
Say I am given a task to soft delete all users that were created before 2020, here is a possible workflow I can use:
- Query my table for a list of IDs to update:
Here I return a list of the IDs for records I want to update. Note that the results here will be a comma separated list, ready for use on the commandline:
SELECT STRING_AGG(id::text, ',') FROM appname_person WHERE created < '2020-01-01' AND deleted = false;
1,2,4
- Next I run the following command to update the
deleted
field:$ python manage.py update_model_field.py Person deleted bool true 1,2,4 Updating 3 record(s) from the model: appname.models.Person The field appname.models.Person.deleted will be updated to: bool(True) Would you like to continue? [y/N]
- After I enter
y
the script completes, I will seeDone
- Allow a file of IDs to be passed as the input IDs. This is more manageable for a large number of IDs.
- Remove rich as a dependency.
- Reference model without use of globals: https://stackoverflow.com/a/75888308
- The lambdas that convert the types as the values for
ValueTypes
:class ValueTypes(enum.Enum): INT = lambda x: int(x) STR = lambda x: str(x) FLOAT = lambda x: float(x) DATETIME = lambda x: datetime.strptime(x, "%m/%d/%y %H:%M:%S") DATETIME_NOW = lambda: datetime.now() NONE = lambda: None
Future updates:
globals
: https://stackoverflow.com/a/75888308