Automatic update of the related model through FK - Django

Asked

Viewed 413 times

0

I am trying to use Signal to do a subtraction update on a table based on the value the user selects. an example situation: On the table Materiais I have the pen material with the fields: codigo_material, descricao_material and quantidade_estoque.

on the table Materiais_Solicitacao picked up the material through a ForeignKey and I also own the field quantidade_aprovada.

I’m trying to make the value passed in the field quantidade_aprovada is subtracted from quantidade_estoque.

models.py Materials

class Materiais(models.Model):
    descricao_material = models.CharField('Descrição do Material', max_length=100)
    codigo_material = models.IntegerField(blank=False, null=False)
    almoxarifado_relacionamento = models.ForeignKey(Almoxarifado, on_delete=models.CASCADE)
    quantidade_estoque = models.FloatField('Quantidade em Estoque', blank=True, null=True)

    class Meta:
        verbose_name = 'Material'
        verbose_name_plural = 'Materiais'

    def __str__(self):
        return self.descricao_material

models.py Materiais_solicitacao

Class Materiais_Solicitacao(models.Model):
        quantidade_material = models.FloatField('Quantidade de Material', null=False, blank=False)
        quantidade_aprovada = models.FloatField('Quantidade Aprovada', blank=True, null=True)
        relacionamento_materiais = models.ForeignKey(Materiais, on_delete=models.CASCADE)
        relacionamento_solicitacao = models.ForeignKey(Solicitacao, on_delete=models.CASCADE)
        unidade_relacionamento = models.ForeignKey(Unidade, on_delete=models.CASCADE)
    class Meta:
        verbose_name = 'Material da Solicitação'
        verbose_name_plural = 'Material das Solicitações'

    def __str__(self):
        return str(self.relacionamento_materiais)

attempt of Signal that I made

def update_estoque(sender, instance, *args, **kwargs):
        materiais = instance.relacionamento_materiais.all()
        estoque_update = 0

        for x in materiais:

            estoque_update = x.quantidade_estoque - instance.quantidade_aprovada
            instance.quantidade_estoque = estoque_update
            print(estoque_update)  

        pre_save.connect(update_estoque, sender=Materiais)

py views.

class SolicitacaoCreateUpdate(PermissionRequiredMixin, UpdateView):
    permission_required = 'administrativo_permissao'
    raise_exception = True
    model = Solicitacao
    template_name = 'administrativo/administrativo_update.html'
    form_class = SolicitacaoForm

    def get_context_data(self, **kwargs):
        context = super(SolicitacaoCreateUpdate, self).get_context_data(**kwargs)
        context['materiaisList'] = Materiais_Solicitacao.objects.all().filter(relacionamento_solicitacao_id=self.object)
        context['materiaisEstoque'] = Materiais.objects.all()
        context['avisos'] = Aviso.objects.all()
        if self.request.POST:
            context['materiais'] = MateriaisFormSet(self.request.POST, instance=self.object)
        else:
            context['materiais'] = MateriaisFormSet(instance=self.object)
        return context

    def form_valid(self, form):
        context = self.get_context_data()
        materiais = context['materiais']
        with transaction.atomic():
            self.object = form.save()

            if materiais.is_valid():
                materiais.instance = self.object
                materiais.save()
        return super(SolicitacaoCreateUpdate, self).form_valid(form)

    def get_success_url(self):
        return reverse('detalheAdministrativo', args=(self.object.pk,))

1 answer

0

I do not see the need to use Signals, in fact when I see someone using always remember a phrase: "Signals is a powerful resource that should never be used". There are several ways to do what you want, I will do as close to your approach (updating by model). For example I created 2 models, a call Item that represents the stock items, with only 2 fields: descricao and quantidade_atual (their names are already descriptive). And another that represents the withdrawals of the items of the stock, with the fields: item which is a FK related to model item, descricao and quantidade representing the quantity to be removed from the stock item.

Note that I overwrite the method save in the model Retirada to subtract the field value quantidade (of the "withdrawal" in progress) of the quantidade_atual of the related item.

Creation of models (app: Core):

from django.db import models

# For DRY
bnull = dict(blank=True, null=True)

class Item(models.Model):
    descricao = models.CharField('Descrição do Material', max_length=100)
    quantidade_atual = models.FloatField('Quantidade em Estoque', **bnull)

    def __str__(self):
        return self.descricao

class Retirada(models.Model):
    item = models.ForeignKey(Item, related_name='Itens', on_delete=models.CASCADE, **bnull)
    descricao = models.CharField('Descricao', max_length=120, **bnull)
    quantidade = models.FloatField('Quantidade retirada', **bnull)

    def save(self, force_insert=False, force_update=False, *args, **kwargs):
        # Salve os dados
        self.item.quantidade_atual -= self.quantidade
        self.item.save()
        super(Retirada, self).save(force_insert, force_update, *args, **kwargs)

After creating the fields in the app, let’s just do the Migrations, no need to create views and neither templates to demonstrate how it works:

Migrations:

$ python manage.py makemigrations
$ python manage.py migrate 

After making the Migrations we go to the Django shell:

$ ./manage.py shell
Python 3.7.2 (default, Dec 29 2018, 06:19:36) 
[GCC 7.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> 

Being in the Django shell we will create 3 stock items, in the bank.

>>> from core.models import Item, Retirada

# Criando 3 objetos do tipo Item
>>> Item.objects.create(descricao='Item 1',quantidade_atual=10)
>>> Item.objects.create(descricao='Item 2',quantidade_atual=10)
>>> Item.objects.create(descricao='Item 3',quantidade_atual=10)

We can now list the stock items:

>>> for i in Item.objects.all():
        print(i.descricao, '|', i.quantidade_atual)

Exit:

Item 1 | 10.0
Item 2 | 10.0
Item 3 | 10.0

Let’s now create some withdrawals from the Items Item 1 and Item 2 randomly:

# Selecionando os itens
item1 = Item.objects.get(descricao='Item 1')
item2 = Item.objects.get(descricao='Item 2')

# Criando as retiradas:
Retirada.objects.create(item=item1, descricao='Retirada 1 Item 1', quantidade=4)
Retirada.objects.create(item=item2, descricao='Retirada 1 Item 2', quantidade=2)
Retirada.objects.create(item=item1, descricao='Retirada 2 Item 1', quantidade=)
Retirada.objects.create(item=item2, descricao='Retirada 2 Item 2', quantidade=4)
Retirada.objects.create(item=item1, descricao='Retirada 3 Item 1', quantidade=1)
Retirada.objects.create(item=item1, descricao='Retirada 1 Item 1', quantidade=1)

Now we will list all withdrawals and current quantities of each item using only the model Retirada:

print('Descricao         | Qt  | Atual','-'*31, sep='\n') 
for r in Retirada.objects.all():
    print(r.descricao, '|', r.quantidade, '|', r.item.quantidade_atual)    

Exit:

Descricao         | Qt  | Atual
-------------------------------
Retirada 1 Item 1 | 4.0 | 1.0
Retirada 1 Item 2 | 2.0 | 4.0
Retirada 2 Item 1 | 3.0 | 1.0
Retirada 2 Item 2 | 4.0 | 4.0
Retirada 3 Item 1 | 1.0 | 1.0
Retirada 1 Item 1 | 1.0 | 1.0

Completion:
You don’t need to use Signals to do what you have in the code, you can do this and more by overriding the method save of the model.

If I may make a few remarks about your code:

  1. You are mixing 2 encoding styles when you define a class, Camelcase and snake_case, the convention for Jango is to use snake_case for most of the code but the class definition is Camelcase;

  2. You can create the name you want for an FK field, but the convention is to use the same name as the related class (in lower case) for example, instead of relacionamento_materias Voce could simply use materiais, keep in mind that in your app this field will be an instance object of the class it points to (or relates to);

  3. Class names should be singular (instead of Materiais, you should use Material), it is true that most courses in colleges and outside recommend using table names in plural, but believe me, in modern Django and FW this ends up generating problems, Django already has a form of automatic "pluralization" (ok, you are not required to use, but break a gallon at the beginning of the development).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.