3

There are two parts of this question for following situation:

I will use example to describe the question:

These are my django models:

class Zone(models.Model):
    zone_name = models.CharField(max_length = 10)
    zone_number = models.CharField(max_length = 10)

    class Meta:
        ordering = ('zone_name',)      

    def __unicode__(self):
        return self.zone_name  


class Stage(models.Model):
    stage_number = models.CharField(max_length = 10)
    stage_name = models.CharField(max_length = 10)
    zones = models.ManyToManyField(Zone, through='ZoneStage')

    class Meta:
        ordering = ('stage_number',)      

    def __unicode__(self):
        return self.stage_number  

    @property
    def value(self):
        return ZoneSubStage.objects.filter(substage__stage=self).aggregate(Sum('value')).get('value__sum', 0)  


class ZoneStage(models.Model):
    zone = models.ForeignKey(Zone)
    stage = models.ForeignKey(Stage)
    value = models.PositiveSmallIntegerField(default=0)

    class Meta:
        ordering = ('zone',)          

    def __unicode__(self):
        return '%s %s' % (self.zone, self.stage)

class SubStage(models.Model):
    sub_name = models.CharField(max_length=10)
    stage = models.ForeignKey(Stage)
    zones = models.ManyToManyField(Zone, through='ZoneSubStage')

    def __unicode__(self):
        return self.sub_name


class ZoneSubStage(models.Model):
    zone = models.ForeignKey(Zone)
    substage = models.ForeignKey(SubStage)
    value = models.PositiveSmallIntegerField(default=0)

    def __str__(self):
        return '%s %s' % (self.zone, self.substage)  

QUESTION 1: How to create automatically new ZoneStage instances for all related Stages if I create manually new Zone instance ?

e.g. I have following Stage instances: S1, S2, S3, S4 and I create new Zone instance "A". So, I want to create automatically new ZoneStage instances AS1, AS2, AS3, AS4 IF such instance has not existed already ?

QUESTION 2: This is the extension to Question 1. If I create new Zone I want to create automatically ZoneStage instances as described in Question 1 plus I want to create automatically ZoneSubStage instances for all SubStages in all Stages if such ZoneSubStage instance has not existed already.

I do not know how to start with it. I though I should read maybe about post_save() first ?

Dariusz Krynicki
  • 2,544
  • 1
  • 22
  • 47

2 Answers2

0

Sub answer : Firstly you don't want to add zone = models.ForeignKey(Zone) in class ZoneStage because you have this information with stage = models.ForeignKey(Stage);

Question 1: you can intercept signal (post_save) after create stage like this.

@receiver(post_save, sender=Stage)
def gen_coupon(sender, instance, raw=True, **kwargs):
    new_zone_stage = ZoneStage(stage=instance)
    new_zone_stage.save()
    return kwargs

Question 2: Same logic but you change sender to current created model object

Mbambadev
  • 729
  • 1
  • 7
  • 26
  • I do not understand your comment about zone field not needed in ZoneStage. If I remove it than I will remove relation to my Zone. Model Stage is related to model Zone via intermediate model ZoneStage via field zone. Model ZoneStage contains field stage which is FK to Stage model. Model ZoneStage contains field zone which is FK to Zone model. It means my ZoneStage model is in the middle between Zone and Stage and this is the only relation. I can not see any other relation between Zone and Stage than via ZoneStage. Correct me if I am wrong. – Dariusz Krynicki Aug 29 '15 at 19:23
  • 1
    first you have zone model ok !
    Now you add Stage Model in this case to access zone model with stage you can use zones or in zone you can use stage_set ok
    now in ZoneStage you can access stage with stage (if you can access stage you can access zone).
    – Mbambadev Aug 29 '15 at 19:39
  • I can see now. I have just had a look at my DB design and can see that in my ZoneStage I need either zone FK or stage FK and not both. – Dariusz Krynicki Aug 29 '15 at 19:43
  • If you have any question me i connected ! post you comment and don't forget to mark my answer ! ok ! – Mbambadev Aug 29 '15 at 19:45
  • do I need to create sender function in class Stage as well and store receiver function in signals.py as well ? – Dariusz Krynicki Aug 29 '15 at 21:07
  • I have placed your @receiver function in models.py and created new Zone instance but new ZoneStage instances have not been created automatically. Am I missing something ? – Dariusz Krynicki Aug 29 '15 at 21:49
  • Yes ! post your code or update your posted question (only code) ! – Mbambadev Aug 29 '15 at 21:57
  • I did manage to connect signals but when I create new zone I get following error: Cannot assign "": "ZoneStage.stage" must be a "Stage" instance – Dariusz Krynicki Aug 29 '15 at 22:16
  • 1
    I really don't think that there shouldn't be 2 foreign keys ( one for `Zone` and one for `Stage`) inside ZoneStage model. `ZoneStage` is an `through` model for `ManyToManyField`, so it's responsible for many to many relation between `Zone` and `Stage`, `ManyToManyField` won't create any additional table for relation, if there is an through method. – GwynBleidD Aug 30 '15 at 07:35
0

I have read about my problem and gathered some knowledge from django manuals. Rakwen advice pointed me towards right direction. This is what I have and this is working at the moment. Although, I do not check if such instance already exists. Furthermore, I have read in two scoop of django it is not good to use signals unless it is really really necessary. I should use model managers instead.

I have created file signals.py and stored it in my app folder next to models.py. It contains following code:

from django.dispatch import Signal


def create_new_zonestage_if_new_zone(sender, **kwargs):
    if kwargs.get('created',False):
        from models import Stage, ZoneStage
        stages = Stage.objects.all()
        for mystage in stages: 
            ZoneStage.objects.get_or_create(stage=mystage,zone=kwargs.get('instance'))
    return kwargs

def create_new_zonesubstage_if_new_zone(sender, **kwargs):
    if kwargs.get('created',False):
        from models import SubStage, ZoneSubStage
        substages = SubStage.objects.all()
        for mysubstage in substages: 
            ZoneSubStage.objects.get_or_create(substage=mysubstage,zone=kwargs.get('instance'))
    return kwargs

I have added following into models.py:

above model classes:

from django.db.models.signals import post_save
from . import signals

below model classes:

post_save.connect(signals.create_new_zonestage_if_new_zone, sender=Zone)
post_save.connect(signals.create_new_zonesubstage_if_new_zone, sender=Zone)

it works.

Dariusz Krynicki
  • 2,544
  • 1
  • 22
  • 47