Django admin showing object ID instead of __str__ when deleting - django

I'm not sure if this is a bug, by design or something I've done.
I'm seeing the same issue as this issue when deleting an item with a M2M relationship where the object ID is returned instead of str.
The str is set correctly and displays correctly otherwise, just not when deleting an item. The object ID shows up instead.
I've tested by creating a new Django project and a simple model with 2 classes related by M2M but get the same thing.
The only way I've found, which isn't ideal, is to use the "through" option on the field.
One I have many relationships like this in various models.
Two, returning str on the intermediate model I have to display both fields so that it makes sense when deleting from either of the related models.
Anyone have any thoughts? Is using "through" the only option?

Related

Is it possible to disable the generation of related objects when deleting an object via Django admin?

I'm currently maintaining a legacy system while a new system is brought up. I noticed recently that I get a timeout when trying to delete certain objects from a specific model. I've tracked this down to being related to the following question which has an accepted answer: Django admin hangs (until timeout error) for a specific model when trying to edit/create
The problem I'm having is that the objects that are related are not directly related from my model in question.
For example I have the following models (generically named to remain vague due to IP at my company):
ModelA which is the model I'm seeing the issue with when deleting from the Django Admin site
ModelB which contains a ForeignKey field to ModelA
ModelC which contains a ForeignKey field to ModelB
ModelD which contains a ForeignKey field to ModelC
ModelE which contains a ForeignKey field to ModelD
Model details:
ModelE can contain tens/hundreds/thousands of entries for any entry of
ModelC. Additionally ModelC can contain tens/hundreds/thousands of entries for any entry of ModelB
Currently when I try to delete ModelA Django attempts to generate all the associated objects all the way down to ModelE which is causing a timeout in certain cases with high number of associated ModelC and ModelE.
Is there a way to avoid this either by overriding a custom template such as delete_confirmation_template or through any other method? Ideally I would like to still show the summary, but I'm not sure that will be possible with the nature of this issue.
A few details for context:
I feel this may be due to a poor overall structure in our DB Schema, but like I mentioned earlier this is a Legacy system.
I do not need an immediate fix for this as I will actually never delete entries for this model except for my current scenario/task of cleaning up duplicated entries(user error not controlled correctly by forms; The forms now check for this) which is being done through a migration script. I simply noticed this when trying to clean up things and leverage this intermediate page as a sanity check when testing said migration script
Timeout screenshot
From discussions in the comments I came to the following conclusion(s):
on_delete being set to CASCADE for models was incorrect and was causing the very high times in retrieving related objects when attempting to delete a model
Since I do not want to allow deleting of these objects when other specific models have associations to them I analyzed and appropriately set on_delete to PROTECT where applicable
Now when I attempt to delete an object it will not allow me to delete the object due to protected related items. It also resolves the timeout issue I was previously observing
This allows me to still delete objects from this model via the Django admin site IF they have no related protected objects, which in the end is the functionality I desire

Why there isn't a "ListField" in Django?

I have a model that has a Charfield (let's name it advantages) with a choices attribute. After a while, I've decided that this field should be "upgraded" to some kind of ListField, since more than one choice can be selected.
From what I have searched, I have two options:
1 - Create a new model, and use a ManyToManyField in the first model referencing this new model. This way, the "multiple select" default field used in admin will be rendered. Life is good.
2- Create a custom field that saves my field as a string with some kind of separator.
These two approaches are summarized in What is the most efficent way to store a list in the Django models? and the 2nd approach in more examples: How to create list field in django, http://cramer.io/2008/08/08/custom-fields-in-django/, https://djangosnippets.org/snippets/1200/, https://djangosnippets.org/snippets/1491/
Fact is: I don't want to create another model just to have the ManyToManyField. This is a controlled list of choices that I have (and don't want people adding new items) and think that creating a table for this is overkill (although I can create a fixture to this table and not register the model in admin.py, so people wouldn't be adding new items. But I don't know how would migrations work when changing these values in fixtures, when in the past I would just chance the choices tuple in my model definition).
...and creating a new custom field, I don't know. This seems like problems in the long run since I don't know the implications, problems when upgrading Django, etc.
Why there isn't a built in ListField? Which problems do you see in the long run for the two approaches I'm thinking of? I'm planning to do the first but I'm a little lost about migrations.
django.contrib.postgres has an ArrayField.
You seem to not be willing to create a new table with only the List inside (because it would be "overkill"), but what you are suggesting is to copy/paste the same exact values in all the entries of your table, which isn't a good model solution in my opinion.
I'd advise to go for the ManyToMany (or any other implementation doing the trick with another table).

fields automatically added by django

By default, Django automatically gives each model an id field.
Are there any additional fields django's ORM adds automatically? Perhaps in specific cases?
There are only 2 other situations I can think of where fields are automatically created. One is when sub-classing another model. The sub-class will inherit the parent's fields, see here. The other is a Many-to-Many relationship. For a M2M relationship not only will a field get created but an entire intermediate table. Again, the relevant docs
Also, you can avoid having Django create the id field if you specify primary=True for the field you want to use as the primary key. See here
There are some other model/DB naming conventions as well. For example, the actual database table names will be prefixed with the Django app name that contains them plus an underscore. For example, a model named Author in an app named library will get called library_author. I'm sure there are other examples as well, so this is not an exhaustive list.

Django removing object from ManyToMany relationship

How would I delete an object from a Many-to-Many relationship without removing the actual object?
Example:
I have the models Moods and Interest.
Mood has a many-to-many field interests (which is a models.ManyToManyField(Interest)).
I create an instance of Moods called my_mood. In my_moods's interests field I have my_interest, meaning
>>> my_mood.interests.all()
[my_interest, ...]
How do I remove my_interest from my_mood without deleting either model instance? In other words, how do I remove the relationship without affecting the related models?
my_mood.interests.remove(my_interest)
Django's Relations Docs
Note: you might have to get an instance of my_mood and my_interest using Django's QuerySet API before you can execute this code.
If you need to remove all M2M references without touching the underlying objects, it's easier to work from the other direction:
interest.mood_set.clear()
While this does not directly address the OP's question, it's often useful in this situation.
In your case you can simply clear the relationship
my_mood.interests.clear()
Then perhaps when you are again creating new relation in your serializer you can do something like this
interests = Interests.objects.get_or_create(name='Something')
my_mood_obj.tags.add(tag[0])
my_mood_obj.save()
model.field.remove(object_you_want_to_remove)
In this case use: my_mood.interests.remove(my_interest)

Smarter removing objects with many-to-many relationship in Django admin interface

I'd like to remove some object with many-to-many relationship using Django admin interface. Standard removing also removes all related objects and the list of removed objects displayed on confirmation page. But I don't need to remove related objects!
Assume we have ContentTopic and ContentItem:
class ContentTopic(models.Model):
name = models.CharField()
code = models.CharField()
class ContentItem(models.Model):
topic = models.ManyToManyField(ContentTopic, db_index=True,\
blank=True, related_name='content_item')
So, I'd like to remove ContentTopic instance using Django admin, but I don't need remove all related ContentItems. So, confirmation page should display only ContentTopic instance to remove.
What is the best way to handle this?
This happens so, coz its developed to do so.
If you want to change this behaviour, the one way can be over-riding delete method of django.db.models.Model.
This delete() method actually does two things, first gathering a list of all dependent objects and delete them. So here, you can override it, to get that list of dependent objects, iterating over it and set their reference to None, instead of deleting them. And thus deleting the concerned object cleanly.
May be if you want this behavior throughout, you can extend a class from django.db.models.Models, override delete(), and extend all your models from this new class.