What is the best way to use `request.user` in a Signal

I am trying to use a Signal to automatically populate a history Model (EngineHistory), when-ever there is a change in a Model (Engine).

From what I read through in Django Forums and Stackoverflow, it is impossible to use the request in the Models,

following is my code:

class Engine(models.Model):
    name = models.CharField(max_length=200, null=True)
    type = models.CharField(max_length=200, null=True)
    model = models.CharField(max_length=200, null=True)
    serial = models.CharField('SERIAL', max_length=20, unique=True, help_text='Serial number of the Engine', null=True)
    boat = models.ForeignKey(Boat, on_delete=models.CASCADE, null=True, blank=True)

class EngineHistory(models.Model):
    engine = models.ForeignKey(Engine, on_delete=models.CASCADE)
    current_boat = models.ForeignKey(Boat, on_delete=models.SET_NULL, null=True, related_name='current_boat')
    previous_boat = models.ForeignKey(Boat, on_delete=models.SET_NULL, null=True, related_name='previous_boat')
    changed_by = models.ForeignKey(Person, on_delete=models.SET_NULL, null=True)
    change_reason = models.TextField(blank=True, null=True)
    change_date = models.DateTimeField(auto_now_add=True)

@receiver(pre_save, sender=Engine)
def track_engine_changes(sender, instance, **kwargs,):
    try:
        original_engine = Engine.objects.get(pk=instance.pk)
        user = Person.objects.get(pk=instance.pk)
    except Engine.DoesNotExist:
        return

    if original_engine.boat != instance.boat:

        EngineHistory.objects.create(
            engine=instance,
            current_boat=instance.boat,  
            previous_boat=original_engine.boat,
            changed_by=user
        )

This code is working good, however in the changed_by in the Signal and EngineHistory model, when I save, it saves the instance of person related to the Engine Model, How can I make it so that it saves the logged in user who made the change.

what is the best way I can go about doing this.

Following is my View for refarence:

class EngineDetailView(LoginRequiredMixin, generic.UpdateView):
    model = Engine
    form_class = EngineUpdateForm
    context_object_name = 'engine'
    template_name = 'engine_details.html'
    
    def get_queryset(self):
        return Engine.objects.select_related('boat', 'gear').all()
    
    def get_success_url(self):
        messages.success(self.request, "Engine details have been updated successfully.")
        return reverse('engine-detail', args=[self.object.id])
    
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        engine_instance = self.get_object()
        history_records = EngineHistory.objects.filter(engine=engine_instance).select_related(
            'current_boat', 'previous_boat', 'engine', 'changed_by'
        ).all()
        context['history'] = history_records
        return context

thanks.

As much I know it can’t be done. The current user is only available via the request, which is not available when using purely model functionality. You can access the user in the View by overriding this method

def put(self, *args, **kwargs):
    return

thanks will have a look at it.

I’m not sure if you have figured this out, and I’m not positive if this will work.
But if you add a field to your Engine Model for last_user_to_update_engine (or something less wordy) and set it to a foreignkey to the User. Then in your view capture the signed in user from the request object and add it to the instance. Then the signed in user instance should be available to you to use like this: changed_by=instance.last_user_to_update_engine in your track_engine_changes fucntion.

Sorry if it doesn’t directly answer your question.

Thanks, yea I did just that, added a new field to the Engine model (update_by) with a ForeignKey to the User model,
and in the views.py I handled it like:

    def form_valid(self, form):
        data = self.request.user
        form.instance.update_by = data
        return super().form_valid(form)

Don’t know if is the correct way, though it solves the problem.