Reputation: 23
i created a proxy model in django 3.1.10 where i defined a function to make a copy of an object with all it's related objects through foreign key and it worked fine , but when i upgraded to django 3.2 the function only create a copy of the object without any related objects
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from ValPlanner.models import ProgramExecution
import datetime
from django.utils import timezone
#-----------------------------------------------------------------------------------------------------------------------------
class Record(models.Model):
title = models.CharField(max_length=50)
description = models.CharField(max_length=200)
execution = models.ForeignKey(ProgramExecution, on_delete=models.PROTECT,null=True)
timestamp = models.DateTimeField(auto_now_add=True)
creator = models.ForeignKey(User, on_delete=models.PROTECT)
record_type = models.CharField(max_length=50,default='template')
def __str__(self):
return self.title
class TemplateRecord(Record):
class Meta:
proxy = True
def create_report(self,creator,execution):
stages = self.stage_set.all();
self.record_type = 'record'
self.pk = None;
self.execution = execution
self.creator = creator
self.save();
for stage in stages :
samples = stage.sample_set.all()
stage.pk = None
stage.stage_type = 'record'
stage.record = self
stage.save();
for sample in samples :
tests = sample.testnum_set.all()
sample.sample_type = 'record'
sample.stage = stage
sample.sample_time = timezone.now()
sample.sampler = creator
for samplenum in range(sample.number_of_samples):
sample.pk = None
sample.save();
for test in tests :
test.pk = None
test.test_type = 'record'
test.sample = sample
test.tester = creator
test.save();
#-----------------------------------------------------------------------------------------------------------------------------
class Stage(models.Model):
record = models.ForeignKey(Record, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
stage_type = models.CharField(max_length=50,default='template')
def __str__(self):
return self.title
#-----------------------------------------------------------------------------------------------------------------------------
class Sample(models.Model):
stage = models.ForeignKey(Stage, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
sample_time = models.DateTimeField(default=timezone.now)
sample_location = models.CharField(max_length=50)
sampler = models.ForeignKey(User, on_delete=models.PROTECT)
sample_type = models.CharField(max_length=50,default='template')
number_of_samples = models.PositiveSmallIntegerField(null=True,default=1)
def __str__(self):
return self.name
#-----------------------------------------------------------------------------------------------------------------------------
class TestNum(models.Model):
sample = models.ForeignKey(Sample, on_delete=models.CASCADE)
name = models.CharField(max_length=50)
result = models.DecimalField(max_digits=15, decimal_places=5,null=True)
acceptance = models.CharField(max_length=50)
maximum_acceptance = models.DecimalField(max_digits=15, decimal_places=5)
minimum_acceptance = models.DecimalField(max_digits=15, decimal_places=5)
test_type = models.CharField(max_length=50,default='template')
tester = models.ForeignKey(User, on_delete=models.PROTECT)
def __str__(self):
return self.name
#-----------------------------------------------------------------------------------------------------------------------------
and my function is in view.py :
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib import messages
from django.contrib.auth.models import User
from django.urls import reverse
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView, TemplateView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin, PermissionRequiredMixin
from .models import Record, TemplateRecord, Stage, Sample, TestNum
import datetime
from django.utils import timezone
class CreateEmptyReport (PermissionView, UpdateView):
model = TemplateRecord
permission_required = 'ETRecord.add_record'
fields = ['execution']
def get_object(self):
template_list = TemplateRecord.objects.filter(record_type='template')
return super().get_object(queryset = template_list)
def form_valid(self,form):
self.object.create_report(creator=self.request.user,execution=form.instance.execution)
return super().form_valid(form);
def get_success_url(self):
return reverse('ETRecord:report_details',kwargs= {'pk': self.object.id})
on old version it ould copy the record and all relaated stages , samples and tests but after update it only copies the record with no associated stages or samples
i tried to uninstall and re-install the older verison it works only on the older version , so how can i make it compatable with the new 3.2 version ?
Upvotes: 2
Views: 49
Reputation: 20539
You are creating the queryset before you copy the record itself, which seems fine
stages = self.stage_set.all();
But... Django won't resolve this queryset before it'll be accessed. Older versions of Django would probably just hold the ID of the parent element, so the queryset doesn't change after you change the ID on the self
, but newer versions of Django are holding the entire object and extracting the ID of the object just before the queryset is actually executed on the database.
This causes your for loop to iterate over stages connected to the new record instance (you've just created it, so it's an empty queryset) instead of the old one.
To fix that issue, you can either force the execution of the queryset earlier (for example by casting it into a list) or by creating the queryset that won't use the self
object, but the ID of the old object explicitly, for example:
stages = Stage.objects.filter(record_id=self.id);
Upvotes: 1