3👍
I think there is no easy way to do that.
As the Django doc says, you can’t associate an item with a relation until it’s been saved. Example from the doc:
>>> a1 = Article(headline='...')
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used.
# should save Article first
>>> a1.save()
# the below statement never know it's just following a creation or not
>>> a1.publications.add(p1)
It’s logically not possible for a relation record to know whether it is added to “a just created item” or “an item that already exists for some time”, without external info.
Some workarounds I came up with:
Solution 1. add a DatetimeField in MyModel
to indicate creation time. m2m_changed handler uses the creation time to check when is the item created. It work practically in some cases, but cannot guarantee correctness
Solution 2. add a ‘created’ attribute in MyModel
, either in a post_save handler or in other codes. Example:
@receiver(post_save, sender=Pizza)
def pizza_listener(sender, instance, created, **kwargs):
instance.created = created
@receiver(m2m_changed, sender=Pizza.toppings.through)
def topping_listener(sender, instance, action, **kwargs):
if action != 'post_add':
# as example, only handle post_add action here
return
if getattr(instance, 'created', False):
print 'toppings added to freshly created Pizza'
else:
print 'toppings added to modified Pizza'
instance.created = False
Demo:
p1 = Pizza.objects.create(name='Pizza1')
p1.toppings.add(Topping.objects.create())
>>> toppings added to freshly created Pizza
p1.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
p2 = Pizza.objects.create(name='Pizza2')
p2.name = 'Pizza2-1'
p2.save()
p2.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
But be careful using this solution. Since ‘created’ attribute was assigned to Python instance, not saved in DB, things can go wrong as:
p3 = Pizza.objects.create(name='Pizza3')
p3_1 = Pizza.objects.get(name='Pizza3')
p3_1.toppings.add(Topping.objects.create())
>>> toppings added to modified Pizza
p3.toppings.add(Topping.objects.create())
>>> toppings added to freshly created Pizza
That’s all about the answer. Then, caught you here! I’m zhang-z from github django-notifications group 🙂
0👍
@ZZY’s answer basically helped me realise that this wasn’t possible without storing additional fields. Fortunately, I’m using django-model-utils which includes a TimeStampedModel
which includes a created
field.
Providing a small enough delta, it was relatively easy to check against the created time when catching the signal.
@receiver(m2m_changed,sender=MyModel.somerelation.though)
def my_signal(sender, instance, created,**kwargs):
if action in ['post_add','post_remove','post_clear']:
created = instance.created >= timezone.now() - datetime.timedelta(seconds=5)
if created:
logger.INFO("The item changed - %s"%(instance) )
- [Django]-Can't build index for solr/haystack: unknown field 'django_id'
- [Django]-How do i show recent posts first in Django blog?
- [Django]-Intersection of two querysets in django
- [Django]-Django: Unknown column 'last_login' in 'field list'
- [Django]-YSlow Best Practices with Django apps, How to implement them?
0👍
For an easier and short way of checking in the object is created or not is using the _state.adding attribute:
def m2m_change_method(sender, **kwargs):
instance = kwargs.pop('instance', None)
if instance:
if instance.adding: #created object
pk_set = list(kwargs.pop('pk_set')) #ids of object added to m2m relation
else:
# do something if the instance not newly created or changed
# if you want to check if the m2m objects is new use pk_set query if exists()
m2m_change.connect(m2m_change_method, sender=YourModel.many_to_many_field.through)
- [Django]-Set serializer field from data value having different names (Custom field Mapping for ModelSerizer)
- [Django]-How to Edit a Database Table Following a Django App Name Change
- [Django]-How do you deploy a flask or django application using jwilder/nginx-proxy?