Ahmed Ezzat
Ahmed Ezzat

Reputation: 23

why model function created on django 3.1.10 doesn't Work on 3.2?

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

Answers (1)

GwynBleidD
GwynBleidD

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

Related Questions