I have a simple Tag model that many other models have a ManyToMany relationship to. The requirement has come up to be able to query/show the most recently used Tags in the system, across all entities that have Tags.
I can add a used_at attribute to the Tag model, and I could order on that. But obviously, the Tag model doesn't get modified when something else just references it, so an auto_now on that attribute won't help me.
Without the use of a through model (which could have an auto_now_add on it), and without performing any invisible (non-django) magic directly in the DB with triggers, is there a sensible way to update the Tag's timestamp whenever a model is saved that references it?
You could use m2m_changed signal
From docs:
Sent when a ManyToManyField is changed on a model instance. Strictly
speaking, this is not a model signal since it is sent by the
ManyToManyField, but since it complements the pre_save/post_save and
pre_delete/post_delete when it comes to tracking changes to models, it
is included here
Related
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
Django docs states:
pk_set
For the pre_add, post_add, pre_remove and post_remove actions, this is a set of primary key values that have been added to or removed from the relation.
For the pre_clear and post_clear actions, this is None.
The difference between the relation after being modified and the current state of the relation was exactly what I needed.
But when trying to use that on django admin I get the whole relation. I think if I wanted it all I would just use the instance parameter.
This looks like it's because clear is being called when I submit the form. So, it's basically wiping out every model and adding again(since keeping track of the model added/remove is a bit annoying). But that way this feature has no significance.
I want to listen for modifications on a manytomany relation and get only the ones that were added. And I'd like to do that on django admin.
If you provide a way to do that, please, also tell me how could that be done using through
Thanks!
In Django, I would like the ability to mark certain model fields as required at the model (or at least database) level, to make sure that I am specifying them explicitly (i.e. not relying on defaults) when creating objects.
Currently, Django lets you designate a model field as required at the forms level (by specifying blank=False in the model). However, it doesn't seem like there is a similar option to get this behavior at the model or database level.
It does seem like there are various hacks to achieve something similar though. For example, you can set the default to something that violates a database constraint. For example, you can do things like:
models.CharField(_('characters'), max_length=4, default=None)
or
models.CharField(_('characters'), max_length=4, default="abcdef")
The former example works when saving to the database since None violates the default not-null constraint of null=False (raising an IntegrityError). The latter works because a DataError is raised when saving. But I don't know if this is guaranteed to work across all databases, etc.
Am I missing something, or is there a better way?
If django models called full_clean() automatically on save(), your check would run at the model level without a form. I've been playing with making this the default behavior in my django projects by creating an auto-clean model subclass which does full_clean() on save(), then deriving my models off that.
If you want to learn why it isn't already like this: Why doesn't django's model.save() call full_clean()?
What is the defacto way of creating model relationships in Django via frontend forms.
For example a user signs up for service using a form, they start a quote.
In getting a quote they can select and add products to their quote specifying variable such as sizes in this process.
This is modelled with relevant User, Quote, Product models and relevant relationships.
I am trying to work out the best way that these are linked together by frontend forms and views.
Would I load into the quote form a hidden field for the related user_id for example, which I can then process manually to form the one-to-many relationship.
I am just wondering if this is something accounted for within forms or if I have to manually create the forms to achieve my goal.
This is one of the more complicated things to try and achieve but there are several things in Django which will help you.
You're going to need a ManyToMany field on the Quote model to link the Products to it.
This can be displayed in forms simply via a ModelMultipleChoiceField:
https://docs.djangoproject.com/en/dev/ref/forms/fields/#modelmultiplechoicefield
... which is just renders a basic multiple select list of existing products.
The interface you want probably looks more like an inline formset however. The complication here is that they are designed for ForeignKey relations rather than ManyToMany.
Under the covers, a ManyToMany relation is actually just two ForeignKey relations, via an intermediate 'through' model. We can exploit this to build an inline formset on the through model, see this answer:
https://stackoverflow.com/a/10999074/202168
You'll note the caveat in that answer, the inline rows won't know which Quote they belong to unless you override some code.
You may like to look at some helper apps which provide custom widgets for ManyToMany fields:
https://code.google.com/p/django-ajax-filtered-fields/
http://django-autocomplete-light.readthedocs.org/en/latest/
Following a related (as yet unanswered) question, I did some investigation and found that the current implementation of Django Haystack's RealTimeSearchIndex makes no attempt to also update on related field (Many to Many) changes. I thought this would be an easy fix - after all, I could just extend RealTimeSearchIndex like this:
class RealTimeM2MSearchIndex(RealTimeSearchIndex):
def _setup_save(self, model):
signals.m2m_changed.connect(self.update_object, sender=model)
signals.post_save.connect(self.update_object, sender=model)
But then I realized (or at least assumed, since it's not working) that this only works if the M2M field is defined on the model itself, and not if it's the "reverse" side of the M2M relationship. Trying to fix that, I then did something like the following:
signals.m2m_changed.connect(self.update_object, sender=model.related_field.through)
Where related_field is the name of the specific Model on other side of the ManyToMany definition. Strangely enough, upon running, Django then complains that the Model has no such field, related_field.
And sure enough, if I inspect the object, Django has not yet extended the model to have the related_field field. If I inspect the same object when displaying a view, however, it does have that related_field.
Summary
So the problem seems to be that Django's automatic behavior to add an attribute to the reverse side of an M2M relationship has yet to happen when Haystack runs its code. How can I overcome this obstacle, and allow Haystack's RealTimeSearchIndex to also update on related field changes?
I think the simplest solution is to just use the built in RealTimeSearchIndex, and add a signal listener in your models.py to reindex the model on m2m_changed, or whenever. See my answer to the other question - you could easily modify it to index on m2m_changed instead of post_save.
Just tried implementing this myself, and your problem is the value of the sender argument in this line:
signals.m2m_changed.connect(self.update_object, sender=model)
I read the documentation for the m2m_changed signal and the sender will be something like MyModel.my_field.through so you need to use that. This means you can't have a generic class as you are trying to do, but will need to define the _setup_save method in each case, with the m2m_changed signal connected for each ManyToMany field that model has.
For example, if your model had two ManyToManyFields called region and sector, you could do:
# we implement these to force the update when the ManyToMany fields change
def _setup_save(self, model):
signals.m2m_changed.connect(self.update_object,
sender=MyModel.sector.through)
signals.m2m_changed.connect(self.update_object,
sender=MyModel.region.through)
signals.post_save.connect(self.update_object, sender=model)
You should also really define the _teardown_save() method:
def _teardown_save(self, model):
signals.m2m_changed.disconnect(self.update_object,
sender=MyModel.sector.through)
signals.m2m_changed.disconnect(self.update_object,
sender=MyModel.region.through)
signals.post_save.disconnect(self.update_object, sender=model)
(This is based on code I have tested, it appears to work - certainly no errors about fields not existing).
Update: Just read your question more closely. Is it possible that your model has the ManyToManyField added dynamically? Is the call to register() after you have defined all your classes?