I'm having an issue where I have a MariaDB event combined with my Django Model. For context, here is my model code:
class PartGroup(models.Model):
GroupID = models.AutoField(primary_key=True, unique=True)
Label = models.CharField(max_length=40, blank=True)
SiteID = models.ForeignKey('Site', on_delete=models.CASCADE, null=True)
InspectionPeriod = models.IntegerField(blank=False, null=True)
LastInspected = models.DateField(blank=True, null=True)
InspectionDue = models.CharField(max_length=255, blank=True)
ConnectedTo = models.ManyToManyField('self', blank=True, null=True)
The fields I want to highlight here are InspectionPeriod
, LastInspected
, and InspectionDue
. I have another model which adds Inspections related to the GroupID
. This contains a date of the inspection:
class GroupInspection(models.Model):
InspectionID = models.AutoField(primary_key=True, unique=True)
GroupID = models.ForeignKey('PartGroup', on_delete=models.CASCADE, null=True, unique=True)
class GroupReport(models.Model):
ReportID = models.AutoField(primary_key=True, unique=True)
InspectionID = models.ForeignKey('GroupInspection', on_delete=models.CASCADE, null=True)
Date = models.DateField(auto_now=False, auto_now_add=False, null=True)
Comment = models.CharField(max_length=255, blank=True)
Signature = models.CharField(max_length=255, blank=True)
I have a MariaDB event to update the LastInspected
field, and from there some code in my view checks that date against the inspection period to see if an inspection is due.
Here's where the problem occurs
I have to have my MariaDB Event updating every 1 second in order to make this work reasonably well. I'm not sure if that's absolutely horrendous performance wise or not, if anyone has another solution that works smoother that's welcome. But that's not the real issue. When I create an inspection, this redirects immediately to my Group template page. This takes less than a second to redirect, which leaves my users confused as they just created an inspection and it doesn't show on the redirect yet unless they immediately refresh after 1 second.
How can I get around this?
EDIT 1 I forgot to mention - the way this works at template level is that I have a page which shows a table view of all my groups and their attributes. I then have a few buttons that create an inspection depending on which button is pressed. This just redirects back to the same page which is why the one second thing is an issue as the redirect usually takes less than a second.
EDIT 2 Here's my view:
def groupList(request, site):
status = "default"
if request.method == "POST":
list = GroupInspection.objects.filter(GroupID = request.POST.get("group"))
if not list.exists():
insp = GroupInspection.create(PartGroup.objects.get(GroupID = request.POST.get("group")))
insp.save()
if 'pass' in request.POST:
groupReport = GroupReport.create(GroupInspection.objects.get(GroupID = request.POST.get("group")), date.today(), "Pass", request.user.username)
groupReport.save()
if 'fail' in request.POST:
groupReport = GroupReport.create(GroupInspection.objects.get(GroupID = request.POST.get("group")), date.today(), "Fail", request.user.username)
groupReport.save()
if 'checkSubmit' in request.POST:
groupReport = GroupReport.create(GroupInspection.objects.get(GroupID = request.POST.get("group")), date.today(), request.POST.get("comments"), request.user.username)
groupReport.save()
status = "changed"
siteselected = Site.objects.get(SiteID = site)
groupList = PartGroup.objects.filter(SiteID = siteselected)
warnings = 0
expired = 0
good = 0
for group in groupList:
if group.LastInspected == None:
group.InspectionDue = "Yes"
expired = expired + 1
else:
Deadline = group.LastInspected + timedelta(days=group.InspectionPeriod)
if datetime.now().date() > Deadline:
group.InspectionDue = "Yes"
expired = expired + 1
elif datetime.now().date() > (Deadline - timedelta(days=30)):
group.InspectionDue = "<30 Days"
warnings = warnings + 1
else:
group.InspectionDue = "No"
good = good + 1
group.save()
context = {
'status': status,
'SiteNo': siteselected.SiteID,
'SiteName':siteselected.SiteName,
'groupList': groupList,
'expired': expired,
'warnings': warnings,
'good': good,
}
template = loader.get_template('moorings/groupList.html')
return HttpResponse(template.render(context, request))
EDIT 3 Here's my SQL Event
CREATE EVENT updateLastInspected
ON SCHEDULE EVERY 1 SECOND
DO
UPDATE proj_partgroup g INNER JOIN ( SELECT i.GroupID_id, max(r.Date) Date FROM proj_groupinspection i INNER JOIN proj_groupreport r ON r.InspectionID_id = i.InspectionID GROUP BY i.GroupID_id ) t ON g.GroupID = t.GroupID_id SET g.LastInspected = t.Date;
You can replace your SQL Event with Django signal that runs after any GroupReport
update, and in case it was new report created - updates corresponding PartGroup
last updated date.
proj/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.apps import apps
GroupReport = apps.get_model("proj", "GroupReport")
PartGroup = apps.get_model("proj", "PartGroup")
@receiver(post_save, sender=GroupReport)
def update_partgroup_lastinspection(sender, instance, created, **kwargs):
if created:
# cause FKs can be null - may need to add
# null checks or try / except
pg = instance.InspectionID.GroupID
if instance.Date > pg.LastInspected:
pg.LastInspected = instance.Date
pg.save(update_fields=['Date'])
# another option to perform same update
PartGroup.objects.filter(
LastInspected__lt=instance.Date,
group_inspection__group_report=instance
).update(
LastInspected=instance.Date
)
proj/apps.py
...
class ProjConfig(AppConfig):
name = "proj"
...
def ready(self):
import proj.signals
You can skip checking that value is greater and just update right away.
Or add logic in model's save()
method instead (which generally is more preferred than signals).
Previous answer - is not the case here, groupList contains changes to instances.
You are returning groupList
which contains old results.
The reason for this is that while being iterated over (for group in groupList
) QuerySets are evaluated with the result being cached in this QuerySet instance and when later it is used in context - this evaluated result is passed, although instances were updated in the database. QuerySets need to be run and be evaluated again to fetch fresh results.
One simple solution to reuse the QuerySet and evaluate it again - is append .all() to previous QuerySet - this will make a copy of provided QuerySet and since it is new one - it is not yet evaluated.
context = {
...
'groupList': groupList.all(),
...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With