Get Related Model From M2M Intermediate Model - django

In signals.py I am catching #receiver(m2m_changed, sender=Manager.employees.through).
This is getting the signal sent when a m2m relationship is created between a Manager and an Employee.
I am trying to get the Employee that this particular relationship is referencing.
I am guessing sender is the 'through' relationship object, but really I'm not sure.
If I print(sender) I get <class 'users.models.Manager_employees'>.
I have tried referenced_employee = sender.employee_id, but this gives me <django.db.models.fields.related_descriptors.ForeignKeyDeferredAttribute object at 0x03616310>.
print(sender['employee_id']) gives me 'ModelBase' object is not subscriptable.
print(sender.employee_id) gives me an error 'ModelBase' object is not subscriptable.
I'm really just trying everything I can think of at this point.
Thank you.

Like sender, the signal also pass other arguments.
#receiver(m2m_changed, sender=Manager.employees.through)
def my_signal_receiver(sender, **kwargs):
# kwargs is a dictionary
for key, value in kwargs.items():
print(key, value)
Take the following example:
an_employee = Employee.objects.create(name='Brenden')
my_manager.employees.add(an_employee)
You will have the following items in the dictionary:
kwargs['instance'] is the instance of the model being changed. In the example above, it will be my_manager
kwargs['model'] is the class being added. In this case Employee and
kwargs['pk_set'] will be {an_employee.id,}, a set of the keys being added, so you could do something like
my_employee = kwargs['model'].objects.get(id=kwargs['pk_set'][0])

Related

Error getting ManyToMany field from an object

How to execute some functionality, after creating an object in the admin area? I'm trying to use post_save signal and trying to get all objects from my field, which has type ManyToMany, I also use sorting package (sortedm2m). When I save the object I try to output this field, but when I create I get an empty queryset, and when I edit I get the old queryset, without the current changes.
class Servers(models.Model):
name = models.CharField(max_length=120, default="name")
content = SortedManyToManyField(Content)
#receiver(post_save, sender=Servers)
def create_server(sender, instance, **kwargs):
print(instance.content.all())
You have to use m2m_changed
Otherwise you can not be able to catch manytomany fields in signal.

Getting model instance by variable model name and pk

I am using Django 1.9, and now trying to override save method behaviour. The problem is that when I do instance.some_field = some_value, the self object is already modified, whereas I need to know that was the original value of some_field. To do this, the only way out seems to be fetching object from database by self's pk.
So I wonder what is GENERIC syntax - if there's any - to fetch instance from the database? By saying GENERIC, I imply that I don't want to explicitly type the model name (like MYMODEL.objects.get(...)), but rather make Django figure out the right model based on target instance's model.
To make the question clearer, let me illustrate my goal in a pseudo-code:
def save_extented(self, *args, **kwargs):
original_object = (self model).objects.get(pk = self.pk)
Is it possible ? Or maybe I don't need this, and there's some smart Django hack to fetch the instance with rolled back field values ?
You can use django-dirtyfields and pre_save signal:
#receiver(pre_save, sender=MyModel)
def pre_save_logic(sender, instance, **kwargs):
if 'my_field' in instance.get_dirty_fields():
do_some_logic()

django model clean issue

Im working in two models ralated via many to many, here's the relevant code:
class Curso(models.Model):
horarios = models.ManyToManyField(Horario, related_name = 'cursos')
...
def clean(self):
...
self.horarios.all()
def save(self,*args,**kwargs):
self.full_clean()
...
Horarios has been already defined, now when i try to create an of curso in the admin interface i get an error pointing to self.horarios.all():
'Curso' instance needs to have a primary key value before a many-to-many relationship can be used.
And it makes sense as it has not been saved, so my problem is, how do i access the value of horarios in the current Curso instance that is being saved?.
thanks in advance
The error seems pretty straight forward to me -- you simply cannot call a ManyToMany before an object is saved.
You can reproduce the error: Curso().horarios
Clearly you can't be doing validation on relationships that can't possibly exist, so simply wrap your call in a if self.pk
if self.pk:
self.horarios.all()

Django pre_save signal

I needed to be able to change my model data before it's saved, so I considered using pre_save handler to be the best option:
#receiver(pre_save, weak = False)
def pre_category_save(sender, **kwargs):
if kwargs['instance'].tags is None:
kwargs['instance'].tags = kwargs['instance'].__unicode__().replace(' -> ', ', ')
Under the instance key of kwargs I expected to find the actual model instance I'm saving, but instead I got an object of LogEntry class - that's the cause why my function fails returning this error: 'LogEntry' object has no attribute 'tags'. So - how can I fix that? Checking if instance has attribute tags is not a solution, because I always get only logentry object. I can eventually overload Model.save method, though I'd rather not do this.
You haven't specified the model class that's being received by this signal, so it's connected itself to all model saves - including LogEntry. Instead, do this:
#receiver(pre_save, sender=MyModel, weak=False)
...
See the documentation.

Getting ID of newly created object in save()

I want to save an object, so that the M2M get saved. Then I want to read out the M2M fields to do some calculations and set a field on the saved object.
class Item(models.Model):
name = models.CharField(max_length=20, unique=True)
product = models.ManyToManyField(SomeOtherModel, through='SomeTable')
def save(self, *args, **kwargs):
super(Item, self).save(*args, **kwargs)
m2m_items = SomeTable.objects.filter(item = self)
# DO SOME STUFF WITH THE M2M ITEMS
The m2m_items won't turn up,. Is there any way to get these up ?
Some confusion here.
Once you've called super, self.id will have a value.
However, I don't understand the point of your filter call. For a start, you probably mean get rather than filter anyway, as filter gets a queryset, rather than a single instance. But even so, the call is pointless: you've just saved it, so whatever you get back from the database will be exactly the same. What's the point?
Edit after question update OK, thanks for the clarification. However, the model's save() method is not responsible for doing anything with M2M items. They need to be saved separately, which is the job of the form or the view.