Solucionado (ver solução)
Solucionado
(ver solução)
3
respostas

Atualizar saldo ao fazer deposito - django

Github: https://github.com/tiagolch/Banco_Chaves

Boa tarde,Estou elaborando uma API de movimentação bancaria e eu gostaria de fazer uma atualização em Contas ao dar entrada em Deposito mas nao estou achando como ... no momento meu codigo esta assim:

classe  Contas ( modelos . Modelo ):
    agencia  =  modelos . CharField ( max_length = 10 )
    conta  =  modelos . CharField ( max_length = 6 )
    saldo  =  modelos . DecimalField ( decimal_places = 2 , max_digits = 30 , default = 0.00 )
    ultima_movimentacao  =  modelos . DateTimeField ( auto_now = True )

    def  __str__ ( self ):
        return  f ' { str ( self . agencia ) } / { str ( self . conta ) } '

    def  get_ultima_movimentacao ( self ):
        retornar a  si mesmo . ultima_movimentacao . strftime ( '% d /% m /% Y% H:% M' )

No codigo acima o campo saldo tem que atualizar ao dar entrada como Deposito com o campo "valor" na tabela abaixo:

classe  Deposito ( modelos . Modelo ):
    conta  =  modelos . ForeignKey ( 'Contas' , on_delete = modelos . CASCADE )
    valor  =  modelos . DecimalField ( decimal_places = 2 , max_digits = 10 , default = 0.00 )
    data_deposito  =  modelos . DateTimeField ( auto_now_add = True )

    def  __str__ ( self ):
        return  f ' { str ( self . conta ) } '

    def  get_data_deposito ( self ):
        retornar a  si mesmo . data_deposito . strftime ( '% d /% m /% Y% H:% M' )

Eu teria que configurar isso em Viewset se nao me engano.... mas nao achei um exemplo ainda...

Segue o ViewSet ate o momento:


from rest_framework import viewsets
from conta.models import Contas, Deposito
from conta.serializer import ContaSerializer, DepositoSerializer


class ContasViewSet(viewsets.ModelViewSet):
    queryset = Contas.objects.all()
    serializer_class = ContaSerializer


class DepositoViewSet(viewsets.ModelViewSet):
    queryset = Deposito.objects.all()
    serializer_class = DepositoSerializer
3 respostas

Oii Tiago, como você está? Espero que esteja bem ^^

Na verdade, para fazermos essa alteração precisamos trabalhar com o nosso modelo (models.py).

Existe um conceito no Django chamado de Signals que serve para enviarmos alguma noticação a outro modelo quando determinado evento ocorrer e é essa estratégia que iremos utilizar. Queremos que quando salvarmos o depósito, a instância de Contas no campo saldo seja atualizado, então podemos utilizar o seguinte trecho de código:

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

@receiver(post_save, sender=Deposito)
def update_saldo(sender, instance, **kwargs):
    instance.conta.saldo += instance.valor
    instance.conta.save()

O que o código acima faz é conectar o método update_saldo a classe de Deposito (sender) sempre que ocorrer um evento de salvamento, e esse evento é denominado por padrão de post_save. Já o parâmetro instance é para que possamos acessar cada atributo dentro da classe Deposito e fazermos a modificação com base na conta em que foi feito o depósito. Você pode ler da seguinte forma: Sempre que um Deposito for salvo, execute a função update_saldo e atualize o saldo da conta.

Veja como fica o código completo:

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

class Contas(models.Model):
    agencia = models.CharField(max_length=10)
    conta = models.CharField(max_length=6)
    saldo = models.DecimalField(decimal_places=2, max_digits=30, default=0.00)
    ultima_movimentacao = models.DateTimeField(auto_now=True)


    def __str__(self):
        return f'{str(self.agencia)}/{str(self.conta)}'

    def get_ultima_movimentacao(self):
        return self.ultima_movimentacao.strftime('%d/%m/%Y %H:%M')


class Deposito(models.Model):
    conta = models.ForeignKey('Contas', on_delete=models.CASCADE)
    valor = models.DecimalField(decimal_places=2, max_digits=10, default=0.00)
    data_deposito = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return f'{str(self.conta)}'

    def get_data_deposito(self):
        return self.data_deposito.strftime('%d/%m/%Y %H:%M')

@receiver(post_save, sender=Deposito)
def update_saldo(sender, instance, **kwargs):
    instance.conta.saldo += instance.valor
    instance.conta.save()

Talvez você esteja se perguntando: Mas é só isso?. Sim, com apenas essa alteração você conseguirá fazer a atualização dos saldos.

E quanto aos testes?

Para testar as alterações acima, eu recomendo que você apague o arquivo do banco de dados: o db.sqlite3. Após isso gere novamente as migrações para que você consiga efetuar os testes do zero e observar a funcionalidade em ação. Após apagar o arquivo, execute os comandos:

python manage.py makemigrations
python manage.py migrate

Em meus testes, peguei como base a seguinte tabela:

tabela

E fiz o cadastro de todas as contas e depósitos exemplificados, como mostro na imagem abaixo:

image

Agora, ao listarmos as contas, perceba que os saldos estão atualizados conforme os depósitos feitos:

image

Outra forma de resolução para esse mesmo problema seria sobreescrever o método save em Deposito utilizando o método update no campo de saldo:

from django.db.models import F

class Deposito(models.Model):
    ... codigo omitido

    def save(self, *args, **kwargs):
        if not self.pk:
            Contas.objects.filter(pk=self.conta_id).update(saldo=F('saldo')+ self.valor)
        super().save(*args, **kwargs)

Para esse caso estamos utilizando a classe F do django, que serve para referenciarmos a coluna saldo diretamente com o valor do banco de dados e somarmos com o valor de depósito.

Qual a melhor abordagem? Nesse caso as duas são equivalentes, porém, emitir sinais é mais abrangente, uma vez que o mesmo aceita uma lista de parâmetros, além de permitir o uso em outros apps.

Continua....

solução!

Uma observação: Pude perceber que o seu atributo get_ultima_movimentacao não está capturando o horário correto e isso está acontecendo por dois motivos:

  • No arquivo de settings.py precisamos explicitar o fuso horário em que estamos, nesse caso, utilizamos o TIME_ZONE = 'America/Sao_Paulo'
  • Também precisamos alterar para False a configuração USE_TZ = False, pois, caso a mesma esteja como True o TIME_ZONE gerado será o padrão do django, ignorando assim o que configuramos para America/Sao_Paulo.

Qualquer dúvida estou por aqui, tá bom?

Grande abraço!

print("Muito Obrigado Nádia, você foi Top")

Isso foi uma Aula inteira, incrível.. Muito obrigado não esperava toda essa qualidade na resposta. Você esta super de parabéns!!!!