pythondjangodjango-simple-history

Django Simple History not logging user


I am using django-simple-history to log changes in my models. I have managed to save log of changes, but I always get None on field history_user.

I am following this tutorial.

This is my model:

class Reservation(models.Model):
    UNCONFIRMED = 'UNCONFIRMED'
    CONFIRMED = 'CONFIRMED'
    CANCELED = 'CANCELED'
    NO_SHOW = 'NO SHOW'
    GO_SHOW = 'GO SHOW'
    STATUS_CHOICES = (
        (UNCONFIRMED, _('Unconfirmed')),
        (CONFIRMED, _('Confirmed')),
        (CANCELED, _('Canceled')),
        (NO_SHOW, _('No show')),
        (GO_SHOW, _('Go show')),
    )

    booking = models.CharField(max_length=25, verbose_name=_('Booking'))
    agency = models.ForeignKey(Agency, on_delete=models.PROTECT, related_name='reservations', verbose_name=_('Agency'))
    comment = models.TextField(null=True, blank=True, verbose_name=_('Comment'))
    status = models.CharField(max_length=15, choices=STATUS_CHOICES, default=UNCONFIRMED, verbose_name=_('Status'))
    confirmation_date = models.DateTimeField(null=True, blank=True, verbose_name=_("Confirmation Date"))
    arrival_date = models.DateField(verbose_name=_('Arrival Date'))
    departure_date = models.DateField(verbose_name=_('Departure Date'))
    confirmation = models.CharField(max_length=15, null=True, blank=True, verbose_name=_('Confirmation Code'))
    is_invoiced = models.BooleanField(default=False, verbose_name=_('Is invoiced?'))
    euroamerica = models.BooleanField(default=False, verbose_name=_("Is Euroamerica sale"))
    user = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT, related_name='reservations')
    changed_by = models.ForeignKey(User, null=True, blank=True, on_delete=models.PROTECT)
    timestamp = models.DateTimeField(null=True, blank=True, auto_now_add=True)
    handle_fee = models.FloatField(null=True, blank=True, verbose_name=_("Handle Fee"))
    log = HistoricalRecords(related_name='history')

    class Meta:
        verbose_name = _('Reservation')
        verbose_name_plural = _('Reservations')
        permissions = (
            ('reservation_can_edit', 'Can Edit Reservation'),
            ('reservation_can_view', 'Can View Reservation'),
            ('reservation_can_view_history_log', 'Can View Reservation History Log')
        )

    def __str__(self):
        return "[{}]{}".format(self.booking, self.agency)

    def clean(self, *args, **kwargs):
        if self.id is None:
            try:
                Reservation.objects.get(booking=self.booking)
            except:
                pass
            else:
                raise CustomValidation(_('Booking already exists.'), 'booking', status_code=status.HTTP_400_BAD_REQUEST)

        if isinstance(self.arrival_date, str):
            raise CustomValidation(_('Arrival date must be a valid date.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)

        if isinstance(self.departure_date, str):
            raise CustomValidation(_('Departure date must be a valid date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)

        if self.arrival_date >= self.departure_date:
            raise CustomValidation(_('Departure date must be later than Arrival date.'), 'departure_date', status_code=status.HTTP_400_BAD_REQUEST)
        # elif self.arrival_date <= timezone.datetime.now().date():
        #     if self.id == None:
        #         raise CustomValidation(_('Arrival date must be later than today.'), 'arrival_date', status_code=status.HTTP_400_BAD_REQUEST)

        if self.status == 'CONFIRMED' and self.confirmation is None:
            raise CustomValidation(_('Must provide a confirmation number.'), 'confirmation', status_code=status.HTTP_400_BAD_REQUEST)

        return True

    def save(self, *args, **kwargs):
        from GeneralApp.models import Parameter

        self.booking = self.booking.replace(' ', '')
        self.booking = self.booking.upper()

        self.full_clean()

        if self.confirmation is not None and self.status is not self.CONFIRMED:
            if self.pk is not None:
                try:
                    previous_reservation = Reservation.objects.get(id=self.pk)
                except:
                    pass
                else:
                    if previous_reservation.status == self.status and previous_reservation.confirmation != self.confirmation:
                        self.status = self.CONFIRMED
            else:
                self.status = self.CONFIRMED

        if self.status == self.CONFIRMED and self.confirmation_date is None:
            self.confirmation_date = timezone.now()

        try:
            self.handle_fee = Parameter.objects.get(name="Handle Fee").value
        except:
            self.handle_fee = None

        super(Reservation, self).save(*args, **kwargs)

    @property
    def _history_user(self):
        return self.changed_by

    @_history_user.setter
    def _history_user(self, value):
        self.changed_by = value

I don't know what I am missing to get the user logged.

EDIT

Here is where I save Reservation:

def save_reservation(self, reservation_data, user, pk):
        try:
            reservation_to_save = models.Reservation.objects.get(booking=pk)
        except:
            raise CustomValidation(_("There is not such reservation {}".format(pk)), 'id', status.HTTP_400_BAD_REQUEST)

        reservation_to_save.booking = reservation_data['booking']
        reservation_to_save.agency = models.Agency.objects.get(id=reservation_data['agency'])
        reservation_to_save.comment = reservation_data.get('comment', None)
        reservation_to_save.status = reservation_data.get('status', 'UNCONFIRMED')
        reservation_to_save.arrival_date = reservation_data['arrival_date']
        reservation_to_save.departure_date = reservation_data['departure_date']
        reservation_to_save.confirmation = reservation_data.get('confirmation', None)
        reservation_to_save.is_invoiced = reservation_data.get('is_invoiced', False)
        reservation_to_save.euroamerica = reservation_data.get('euroamerica', False)

        reservation_to_save.save()

        return reservation_to_save

Solution

  • According to the documentation you have to add HistoryRequestMiddleware to your middleware in settings.py

    Like so:

    MIDDLEWARE = [
        # ...
        'simple_history.middleware.HistoryRequestMiddleware',
    ]
    

    This at least was the solution in my case.

    If you don't want to use this middleware you can set the user manually, see this.