I have a model with a ForeignKey field to another model. I no longer want or need this ForeignKey field but there appears to be no clear way to successfully drop the field using the ORM API.
I’ve tried to simply remove the field or copy it over to another empyt TextField, then delete however, every time I try to makemigrations I get an error “(admin.E202) ‘ModelTwo’ has no ForeignKey to ‘ModelOne’”.
Is there a simple way to drop the foreign key constraint and delete this field using the django ORM/API? Or will I need to use SQL commands to perform this action?
Hi, this would be easier if we could see your relationships because we don’t know which FK model was created first and therefore which is dependent on which. The answer may change depending on this. For now, try this and let me know if it works.
Easy way: If you don’t need to save any of the data in your database and can start over. Delete the attribute in your model with the FK you don’t want. Delete your migrations in the migrations folders in each app. Make migrations again.
If you don’t want to delete your data:
- Add (, on_delete=models.CASCADE), after the FK that you want to delete. Make migrations again. Report back if this worked or not.
on_delete=SET_NULL. This would leave data alone; it just would just set the FK’s to null, destroying the relationships. Add
null=True on that FK as well.
Let us know if this worked.
What would be the best way to show this relationship where you would know which was created first?
In my example it would look like this:
software_key = models.ForeignKey(SomeOtherModel, on_delete=models.CASCADE)
machine_id = models.TextField()
machine_id = models.ForeignKey(ModelOne, on_delete=models.CASCADE)
username = models.TextField()
And to clarify, I would like to drop ‘machine_id’ from ModelTwo.
I’d like to keep the data so I’ll try your second and third options and report back.
Having machine_id in ModelTwo make things confusing. Just call it machine and SQL will add id at the end automatically to show it’s a FK.
Part 1: Seeing the issue. Slightly unrelated to your question, but kind of can lead you to find answers.
- Download SQLite Viewer in VSCode.
- Click the dq.sqlite3 file in the main project folder.
- Click on the modeltwo table.
- Look how machine is called machine_id_id
Part 2: Fix this.
- Change machine_id to machine in ModelTwo.
- Run Migrations.
- Go back to the dq.sqlite3 tab and hit refresh.
- Go back to modeltwo table.
- Look how id is already added to machine.
Do not have a Username field in another Model other than the User model. All user data should be in one table or you will create data redundancy. This is not fun to fix.
Think of things like this:
You have a User model. You have Classes that spiderweb off of it. Then Classes that spider web off of those classes. You can access all usernames if you go back down the spiderweb. All usernames are in User. User > ModelOne > ModelTwo. You are making it confusing because you are calling them Model’s and not what they actually are. . . what data they store.
If you want to get a username you just access User. If you want to get a username’s from a certain modelyou use filter’s.
Let’s says ModelOne is a Business and you want username’s just from that business. First make sure there is a FK to User in Business. In Business, you have a user_id column that links to User and maybe a title attribute. Filter for only the business titles you want. Then access the usernames from User for that business. I can’t put exactly how to do it here because it depends on project set up. I keep updating this answer, so I don’t mislead you. I don’t want to add to much because I don’t know how you want your project to work. But we always have one place we store user info and branch off of that.
I appreciate your feedback and suggestions. This is super helpful. I was simplifying all the names to show an example and not what I actually use in my code. I do use username elsewhere and I can see how that’s not cool. I’ll work to fix this.
Back to your suggestions: At the end of the day, it does not sound like I can simply drop the machine_id field(column) in ModelTwo. I want to disassociate it with ModelOne essentially and give it a FK from another model. My end goal would be to drop ModelOne eventually as it’s losing its value or relevance. Aside from the data loss option, the other two options don’t appear to enable to removal of the field entirely. Just how the row is handled if I deleted a related row from ModelOne.
But before you or perhaps anyone else responds, I will try to help clarify a more accurate picture of what the models look like in an edit to this post.
Having more info would help.
There is another techie solution to this, but you might not like it.
- Download DBeaver and connect your DB.
- Download your SQLite File as a CSV, edit your CSV in excel or w.e and delete the things you don’t want.
- Upload the csv to DBeaver.
- Make sure you Django model’s and columns reflect what’s in DBeaver. Or work through the errors if they don’t.
There are directions for all of these steps on Google, not on one web page though. Way too many steps to go into detail here.
You could also keep the Class and FK and just add a related_name = field in the parentheses, using it for another model in the future.
Updated Model for Example:
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(Profile, on_delete=models.CASCADE)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
date_activated = models.DateField(blank=True, null=True, default=None)
software_key = models.ForeignKey(SoftwareKey, on_delete=models.CASCADE)
machine_id = models.TextField()
is_active = models.BooleanField(default=True)
created_on = models.DateTimeField(auto_now_add=True)
machine = models.ForeignKey(Activation, on_delete=models.SET_NULL, null=True)
software_key = models.TextField(default=None, blank=True, null=True)
datetime_ran = models.DateTimeField(auto_now_add=True)
application_version = models.TextField()
files_processed = models.IntegerField()
My goal is to delete/drop the ‘machine’ field from ‘ProcessingRuns’ and instead make the FK set to ‘software_key’ field on ‘SoftwareKey’ model. I’d like to eventually drop the ‘Activations’ model altogether but not something we would need to cover here.
Basically it sounds like just deleting this field
machine = models.ForeignKey(Activation, on_delete=models.SET_NULL, null=True) is not possible using the django ORM/API. But can be possible by manually running a few SQL commands to remove the FK constraint and ultimately dropping the column.
This sounds like a great thing to be able to do.
DBeaver > table> ForeignKeysFolder to get the name of FK. Usually looks something like this: users_table_name_attribute_id_061b3495_fk_user
ALTER TABLE tbl_name DROP FOREIGN KEY name_of_fk_from_folder;
Lastly, go to Django migrations folder’s and go through every migration file. Delete anything that shows that table or attribute was even made.
Awesome, I will give this a try and report back.
I greatly appreciate your help on this.
You say in your original post that you’re getting an
admin.E202 error. This means that you have an
InlineModelAdmin subclass (in an
admin.py) that you need to edit to stop requiring the
ForeignKey you are trying to remove.
Once you’ve fixed this admin issue (and any other issues) Django will be able to generate a suitable migration to remove the field.
This solved my issue and thank you for sharing that link! It makes total sense now.
I’m surprised that link did not pop up on my searches with that error code. Bookmarking that one for later.
Just to recap, all I had to do was drop the field in my model, remove a connection between the two models in my admin configuration and Django successfully generated migration to remove the field.