Guys I want to create a tools inventory app.
I have this method that will do the exact same thing on all tools types. What I want to know is how can I use this method dynamically selecting what models to use (maybe how to pass a model class as an argument).
It's currently working only for one type of tool. My models.py has an abstract model that has all common fields for all tools and then various models that inherit it for tool specific fields (e.i. End mills, Drill-bits, pliers, screwdrivers, etc all inherit common filed from my abstract model).
def calcular_nueva_cantidad(ce, up, get_link):
if get_link == 'incremento':
total = ce + up
else:
total = -(ce - up)
return total
def calcular_nuevo_total(nce, pu):
total = nce * pu
return total
# crea el formulario para la actualizacion de cantidad existente
class updateForm(forms.Form):
update = forms.IntegerField()
def actualizar_cantidad(request, pk, model ):
# trae de la base de datos el valor de la cantidad_existente
cantidad_existente = model.objects.filter(pk=pk).values('cantidad_existente')
c = cantidad_existente.values_list('cantidad_existente', flat=True)
ce = c[0]
# trae de la base de datos el valor de la precio_unitario
precio_unitario = model.objects.filter(pk=pk).values('precio_unitario')
p = precio_unitario.values_list('precio_unitario', flat=True)
pu = p[0]
# trae de la base de datos el valor de la total
qs_total = model.objects.filter(pk=pk).values('total')
if request.method =='POST':
form = updateForm(request.POST)
if form.is_valid():
# Obtiene el name de urls para el link segun sea el caso
get_link = resolve(request.path_info).url_name
get_linkwargs = resolve(request.path_info).kwargs
print(F'========>{get_link, get_linkwargs }<=========')
up = form.cleaned_data['update']
# Calcula el nuevo valor de cantidad existente
nce = calcular_nueva_cantidad(up, ce, get_link)
# Actualiza la nueva cantidad existente
cantidad_existente.update(cantidad_existente=nce)
# Calcula el nuevo valor de cantidad existente
s_total = calcular_nuevo_total(nce, pu)
# Actualiza la nueva cantidad existente
qs_total.update(total=s_total)
# Obteiene item id del tipo de cortador asi puede regresar a la pantalla del listado
pp = model.objects.filter(pk=pk).values('tipo')
ppp = pp.values_list('tipo', flat=True)
pk = ppp[0]
return HttpResponseRedirect(reverse('inventario:cortadores-list', args=(pk, )))
else:
# Redirect to fail page after POST
return HttpResponse('')
else:
form = updateForm()
return render(request, 'inventario/update.html', {'form':form})
This is my abstract class in my models.py
class Item(models.Model):
description = models.CharField(max_length=30,)
numero_parte = models.CharField(max_length=30)
proveedor = models.ForeignKey(Proveedor, on_delete=models.CASCADE)
cantidad_existente = models.PositiveIntegerField()
update = models.PositiveIntegerField(blank=True, default=0)
cantidad_minima = models.PositiveIntegerField()
precio_unitario = models.DecimalField(max_digits=7, decimal_places=2)
total = models.DecimalField(max_digits=7, decimal_places=2, blank=True)
asignado_a = models.ForeignKey(Empleados, on_delete=models.CASCADE, blank=True, null=True)
anaquel = models.CharField(max_length=2, choices=ANAQUEL, blank=True, null=True)
posicion_en_x = models.CharField(max_length=2, blank=True, null=True)
posicion_en_y = models.CharField(max_length=2, blank=True, null=True)
activo = models.BooleanField()
class Meta:
abstract = True
def save(self,*args,**kwargs):
self.total = self.cantidad_existente * self.precio_unitario
super().save(*args,**kwargs)
These two classes inherit from Item
class Cortadores(Item):
tipo = models.ForeignKey(Tipos_Cortadores,on_delete=models.CASCADE)
material = models.ForeignKey(Materiales, on_delete=models.CASCADE)
filos = models.CharField(max_length=5, choices=GABILANES)
diametro = models.ForeignKey(Diametros, on_delete=models.CASCADE)
longitud = models.ForeignKey(Longitud, on_delete=models.CASCADE)
desbaste = models.CharField(max_length=1, choices=DESBASTE)
class Meta:
verbose_name_plural = "Cortadores"
def get_absolute_url(self):
return reverse('inventario:cortadores-list', kwargs={'id': self.tipo.id})
def __str__(self):
return '%s %s %s %s %s %s' % ( str(self.tipo), str(self.material), str(self.filos), str(self.diametro),
self.longitud, self.desbaste
)
class Tornillos(Item):
tipo = models.CharField(max_length=10, choices=TIPO_TORNILLO)
paso = models.ForeignKey(Paso_Tornillo, on_delete=models.CASCADE)
material = models.ForeignKey(Materiales, on_delete=models.CASCADE)
longitud = models.ForeignKey(Longitud, on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "Tornillos"
def get_absolute_url(self):
return reverse('inventario:lista-herramientas-mecanicas')#, kwargs={'pk': self.pk})
def __str__(self):
return '%s %s %s %s' % (str(self.tipo), str(self.paso), str(self.material), str(self.longitud))
I made a class for every tool type that I own. I only included these two classes for simplicity and not fill the post with a lot of code.
If you still can change the design of your tables, you could use multi-table-inheritance instead of the abstract-base-class you are using now.
That would mean that you will have one model Item (which will be an actual database table) and then other models that inherit from this model (their database tables will only hold the new fields, but not repeat any fields).
For that change you only need to remove abstract = True from Item.Meta; you should not need to change other parts form your models. Of course, you will have to drop your tables and run migrations again (will destroy your current data).
As I said, this is only an option if you can still change your database tables significantly.
With this setup, you can query your Item model directly for the common fields. There are some subtleties you have to be careful about, but read the docs I linked to see if it solves your issue.
Related
I have override init in RandomisationFOrm and have an error I did not have before
I understand the error: in my models Randomization ran_num field is UNIQUE
and when I try to save a form when I call randomisation_edit view with the same ran_num it raise error
but before overriding __init__, this error was manage by Django itself
I try to add validation as below but raise another error: 'RandomisationForm' object has no attribute 'clean_data' -> I do the same for other field without this error...
def clean_ran_num(self):
data = self.clean_data['ran_num']
if data == 'CIV-CO-001':
raise forms.validationError('test')
return data
I try to add validation in the global 'clean' method but it do not apply...
I do not understand...
models.py
class Randomisation(models.Model):
# ran_num = models.CharField("Patient code", max_length=10, unique=True, null=True, blank=True,validators=[validate_patient_code])
ran_ide = models.AutoField(primary_key=True)
ran_num = models.CharField("Patient code", max_length=10, unique=True, null=True, blank=True)
ran_dat = models.DateField("Date of randomization", null=True, blank=True)
ran_inv = models.CharField("Investigator", max_length=20, null=True, blank=True)
ran_pro = models.IntegerField("Procedure used to randomized", null=True, blank=True)
ran_pro_per = models.CharField("if telephone, name of the person reached on the phone", max_length=20, null=True, blank=True)
ran_crf_inc = models.IntegerField("Was the CRF inclusion 2 Eligibility fulfilled?", null=True, blank=True)
ran_tbc = models.IntegerField("Is TB diagnosis possible, probable or definite?", null=True, blank=True)
ran_crf_eli = models.IntegerField("Was the CRF Inclusion 2 Eligibility fulffiled?", null=True, blank=True)
ran_cri = models.IntegerField("Is the conclusion All criteria fulfilled for randomisation", null=True, blank=True)
ran_sta = models.IntegerField("British Medical Council Staging (worst stage reported between first symptoms and now)", null=True, blank=True)
ran_vih = models.IntegerField("HIV/AIDS status", null=True, blank=True)
ran_bra = models.IntegerField("TB treatment assigned", null=True, blank=True)
ran_med = models.CharField("Batch number", max_length=6, null=True, blank=True)
ran_log_dat = models.DateTimeField("Date", null=True, blank=True)
ran_log = models.CharField("Name of the person who performs randomisation on site", max_length=10, null=True, blank=True)
forms.py
class RandomisationForm(forms.ModelForm):
# surcharge méthode constructeur (__init__) pour avoir accès aux variables de sessions
# https://stackoverflow.com/questions/3778148/django-form-validation-including-the-use-of-session-data
def __init__(self, request, *args, **kwargs):
self.request = request
super(RandomisationForm, self).__init__(*args, **kwargs)
# vérification que le numéro patient saisi correspond bien au site actuellement sélectionné
# i.e. profil 'International'
def clean(self):
data = self.cleaned_data['ran_num']
if data[4:6] != self.request.session.get('selected_site'):
raise ValidationError(_('Patient code is different from the selected site'))
TYPES = [
(None, ''),
(1, _('On-line')),
(2, _('Telephon')),
]
YESNO = [
(None, ''),
(1, _('Yes')),
(0, _('No')),
]
TB = [
(None, ''),
(1, _('Possible')),
(2, _('Probable')),
(3, _('Definite')),
]
SEVERITY = [
(None, ''),
(1, _('Mild')),
(2, _('Severe')),
]
HIV = [
(None, ''),
(0, _('Negative')),
(1, _('Positive')),
]
ran_dat = forms.DateField(
label = _("Date of randomization"),
initial = datetime.datetime.now,
required = True,
)
ran_num = forms.CharField(label = _("Patient number"), required=True) # ajout
ran_inv = forms.CharField(label = _("Investigator"), required=True)
ran_pro = forms.ChoiceField(label = _("Type"), widget=forms.Select, choices=TYPES)
ran_pro_per = forms.CharField(label = _("if telephone, name of the person reached on the phone"), required=False)
ran_crf_inc = forms.ChoiceField(label = _("Was the CRF inclusion 1 TB diagnosis fulfilled?"), widget = forms.Select, choices = YESNO)
ran_crf_eli = forms.ChoiceField(label = _("Was the CRF Inclusion 2 Eligibility fulffiled?"), widget = forms.Select, choices = YESNO)
ran_tbc = forms.ChoiceField(label = _("Is TB diagnosis possible, probable or definite?"), widget = forms.Select, choices = TB)
ran_cri = forms.ChoiceField(label = _("Is the conclusion All criteria fulfilled for randomisation"), widget=forms.Select, choices = YESNO)
ran_sta = forms.ChoiceField(label = _("British Medical Council Staging (worst stage reported between first symptoms and now)"), widget = forms.Select, choices = SEVERITY)
ran_vih = forms.ChoiceField(label = _("HIV/AIDS status"), widget = forms.Select, choices = HIV)
class Meta:
model = Randomisation
# Tous les champs sauf les champs de log et les champs TB treatment et Drug batch number
fields = ('ran_num','ran_dat','ran_inv','ran_pro','ran_pro_per','ran_crf_inc','ran_tbc','ran_crf_eli','ran_cri','ran_sta','ran_vih',)
def clean_ran_crf_inc(self):
data = self.cleaned_data['ran_crf_inc']
if int(data) == 0:
raise forms.ValidationError(_("This criteria is mandatory for randomization"))
return data
def clean_ran_crf_eli(self):
data = self.cleaned_data['ran_crf_eli']
if int(data) == 0:
raise forms.ValidationError(_("This criteria is mandatory for randomization"))
return data
def clean_ran_cri(self):
data = self.cleaned_data['ran_cri']
if int(data) == 0:
raise forms.ValidationError_(("This criteria is mandatory for randomization"))
return data
views.py
def randomisation_edit(request):
if request.method == "POST":
form = RandomisationForm(request, data=request.POST or None)
if form.is_valid():
randomisation = form.save()
# randomisation
rand = Randomiser(randomisation.ran_num, randomisation.ran_vih, randomisation.ran_sta)
# Mise à jour de la base de données -> Patient code pour rendre indisponible la ligne
# pour une prochaine randomisation
# ListeRandomisation
# 1.Récupération de la ligne dans la liste de rando
bras = rand['bras']
# 2.Mise à jour de la ligne dans la liste de rando
patient = ListeRandomisation.objects.get(ran_ide = bras)
patient.ran_dis = randomisation.ran_num # pat
patient.save()
# Medicament
# 1.Récupération de la ligne dans la liste de correspondance médicament
medicament = rand['medicament']
# 2.Mise à jour de la ligne dans la liste de correspondance médicament
medicament = Medicament.objects.get(med_ide = medicament)
medicament.ran_dis = randomisation.ran_num # pat
medicament.save()
# mise à jour de la variable de session can_randomize
request.session['can_randomize'] = is_randomizable(medicament.ran_dis)
# Mise à jour de la table Randomisation avec le bras et le numéro de boite attribués
randomisation.ran_bra = patient.ran_bra
randomisation.ran_med = medicament.med_num
randomisation.ran_log_dat = datetime.now()
randomisation.ran_log = request.user.username
randomisation.save()
return redirect('randomization:confirmation', pk = randomisation.pk)
else:
form = RandomisationForm(request)
return render(request, 'randomization/randomisation_edit.html', {'form': form})
When you override the Form.clean method you have to call super(RandomisationForm, self).clean() or you will loose the unique constraint checks.
From the docs:
https://docs.djangoproject.com/en/3.0/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other
The call to super().clean() in the example code ensures that any validation logic in parent classes is maintained. If your form inherits another that doesn’t return a cleaned_data dictionary in its clean() method (doing so is optional), then don’t assign cleaned_data to the result of the super() call and use self.cleaned_data instead:
I have three related models and I need to save all instances in a single transaction.
A simplified version of my models are:
class Recibosventas(models.Model):
idreciboventa = models.AutoField(primary_key=True)
idemplea = models.ForeignKey(Empleados, models.DO_NOTHING, db_column='idemplea', blank=True, null=True)
idempres = models.SmallIntegerField()
categocompro = models.CharField(max_length=2)
tipocompro = models.CharField(max_length=1)
puntoventa = models.SmallIntegerField()
numero = models.IntegerField(blank=True, null=True)
class Meta:
managed = True
db_table = 'recibosventas'
unique_together = (('idempres', 'categocompro', 'tipocompro', 'puntoventa', 'numero'),)
class Fondos(models.Model):
idfondo = models.AutoField(primary_key=True)
idemplea = models.ForeignKey(Empleados, models.DO_NOTHING, db_column='idemplea', blank=True, null=True)
idtipofondo = models.ForeignKey('Tiposfondo', models.DO_NOTHING, db_column='idtipofondo')
idconcepfondo = models.ForeignKey(Conceptosfondos, models.DO_NOTHING, db_column='idconcepfondo')
idreciboventa = models.ForeignKey('Recibosventas', models.DO_NOTHING, db_column='idreciboventa', related_name='fondoreciboventa', blank=True, null=True)
class Meta:
managed = True
db_table = 'fondos'
class Movimientosfondos(models.Model):
idmovimifondo = models.AutoField(primary_key=True)
idfondo = models.ForeignKey(Fondos, models.DO_NOTHING, db_column='idfondo', related_name='movimientosfondos', blank=True, null=True)
iddispon = models.ForeignKey(Disponibilidades, models.DO_NOTHING, db_column='iddispon')
class Meta:
managed = True
db_table = 'movimientosfondos'
serializers.py:
class MovimientosfondosSerializer(serializers.ModelSerializer):
class Meta:
model = Movimientosfondos
fields='__all__'
class FondosSerializer(serializers.ModelSerializer):
movimientosfondos = MovimientosfondosSerializer(many=True)
class Meta:
model = Fondos
fields='__all__'
def create(self, validated_data):
with transaction.atomic():
# Crear la instancia de pedidos de venta
movimientosfondos_data = validated_data.pop('movimientosfondos')
fondos = Fondos.objects.create(**validated_data)
# crear cada instancia de movimientosfondos
for movimientosfondos_data in movimientosfondos_data:
movimientosfondos_data['idfondo'] = fondos
Movimientosfondos.objects.create(**movimientosfondos_data)
return fondos
def update(self, instance, validated_data):
with transaction.atomic():
# Crear la instancia de pedidos de venta
movimientosfondos_data = validated_data.pop('movimientosfondos')
# Eliminar el detalle de ventas
for movimientosfondos in (instance.movimientosfondos).all():
movimientosfondos.delete()
# crear nuevamente cada instancia de ventasarticulos
for movimientosfondos_data in movimientosfondos_data:
movimientosfondos_data['idfondo'] = instance
Movimientosfondos.objects.create(**movimientosfondos_data)
return instance
class RecibosventasSerializer(serializers.ModelSerializer):
fondoreciboventa = FondosSerializer(many=True)
class Meta:
model = Recibosventas
fields='__all__'
def create(self, validated_data):
with transaction.atomic():
# Crear la instancia de pedidos de venta
fondosreciboventa_data = validated_data.pop('fondoreciboventa')
recibosventas = Recibosventas.objects.create(**validated_data)
# crear cada instancia de fondos
for fondoreciboventa_data in fondosreciboventa_data:
fondoreciboventa_data['idreciboventa'] = recibosventas
Fondos.objects.create(**fondoreciboventa_data)
return recibosventas
def update(self, instance, validated_data):
with transaction.atomic():
# Crear la instancia de pedidos de venta
fondosreciboventa_data = validated_data.pop('fondoreciboventa')
for fondoreciboventa in (instance.fondoreciboventa).all():
fondoreciboventa.delete()
# crear nuevamente cada instancia de pedidoventasarticulos
for fondoreciboventa_data in fondosreciboventa_data:
fondoreciboventa_data['idreciboventa'] = instance
Fondos.objects.create(**fondoreciboventa_data)
return instance
apiviews:
class FondosList(generics.ListCreateAPIView):
queryset = Fondos.objects.all()
serializer_class = FondosSerializer
def perform_create(self, serializer):
idemplea = Empleados.objects.get(usuariweb = self.request.user)
serializer.save(idemplea=idemplea)
class RecibosventasList(generics.ListCreateAPIView):
queryset = Recibosventas.objects.all()
serializer_class = RecibosventasSerializer
def perform_create(self, serializer):
idemplea = Empleados.objects.get(usuariweb = self.request.user)
serializer.save(idemplea=idemplea)
class RecibosventasDetalle(generics.RetrieveDestroyAPIView):
queryset = Recibosventas.objects.all()
serializer_class = RecibosventasSerializer
def perform_destroy(self, instance):
with transaction.atomic():
# Eliminar el detalle de pedidosventas
for movimientosfondos in (instance.fondoreciboventa.movimientosfondos).all():
movimientosfondos.delete()
instance.fondoreciboventa.delete()
# Eliminar el encabezado pedidosventas
instance.delete()
class RecibosventasUpdate(generics.UpdateAPIView):
queryset = Recibosventas.objects.all()
serializer_class = RecibosventasSerializer
urls:
urlpatterns = [
path('v1/recibosventas/', RecibosventasList.as_view(), name='recibosventas_list'),
path('v1/recibosventas/<int:pk>', RecibosventasDetalle.as_view(), name='recibosventas_detalle'),
path('v1/recibosventasupdate/<int:pk>', RecibosventasUpdate.as_view(), name='recibosventas_update'),
]
I send to the api:
{
"idreciboventa": null,
"idemplea" : 1,
"fondoreciboventa": [
{
"idfondo": 1,
"idemplea" : 1,
"movimientosfondos": [
{
"idmovimifondo": 1,
"idfondo": 1,
"iddispon": 9,
}
],
"idtipofondo": 1,
"idconcepfondo": 2,
}
]
"categocompro": "RC",
"tipocompro" : "F",
"puntoventa": 1,
"numero" : 1,
}
I get the following error:
Direct assignment to the reverse side of a related set is prohibited. Use movimientosfondos.set() instead.
I don't understand what I'm doing wrong. Somebody could help me please?
I'm trying to update field in my models with the newly calculated value from my view and when I try to update my field using total.objects.update(total=total) this is what i get 'decimal.Decimal' object has no attribute 'objects'.
I also have some other questions, I'm currently calculating the total field from initial values inputted when the item is created, using the save() method. is this a good idea ? or is there a better way of doing this? the reason I'm currently doing this is because i display the a list of items with their initial values.
Over all I'm trying to make a tools inventory system. I also have some other concerns.
How can I use this view on multiple items types? the model items is an abstract model that contains all common fields,and every tool type inherits this model and will be using the same operations, calculate the new cantidad_existente and update the total fields. Is there a better way?
my views.py
def calcular_nueva_cantidad(ce, up):
total = ce + up
return total
class updateForm(forms.Form):
update = forms.IntegerField()
def actualizar_cantidad(request, pk):
# trae de la base de datos el valor de la cantidad existente
cantidad_existente = Cortadores.objects.filter(pk=pk).values('cantidad_existente')
c = cantidad_existente.values_list('cantidad_existente', flat=True)
ce= c[0]
# trae de la base de datos el valor de precio_unitario
precio_unitario = Cortadores.objects.filter(pk=pk).values('precio_unitario')
p = precio_unitario.values_list('precio_unitario', flat=True)
pu =p[0]
# trae de la base de datos el valor de la total
total = Cortadores.objects.filter(pk=pk).values('total')
print(F'el precio unitario es {total} ----------------------------------------------------')
if request.method =='POST':
form = updateForm(request.POST)
if form.is_valid():
up = form.cleaned_data['update']
nce = calcular_nueva_cantidad(up, ce)
total = nce * pu
print(F' el nuevo total es {total} -----------------------')
# nce.save()
cantidad_existente.update(cantidad_existente=nce)
total.objects.update(total=total)
return render(request, 'inventario/cortadores.html', {'nce':nce})
else:
# Redirect to fail page after POST
return HttpResponse('')
else:
form = updateForm()
return render(request, 'inventario/update-cortador.html', {'form':form, 'cantidad_existente':cantidad_existente })
my models.py
class Item(models.Model):
description = models.CharField(max_length=30,)
numero_parte = models.CharField(max_length=30)
proveedor = models.ForeignKey(Proveedor, on_delete=models.CASCADE)
cantidad_existente = models.PositiveIntegerField()
update = models.PositiveIntegerField(blank=True, default=0)
cantidad_minima = models.PositiveIntegerField()
precio_unitario = models.DecimalField(max_digits=5, decimal_places=2)
total = models.FloatField(blank=True)
asignado_a = models.ForeignKey(Empleados, on_delete=models.CASCADE, blank=True, null=True)
anaquel = models.CharField(max_length=2, choices=ANAQUEL, blank=True, null=True)
posicion_en_x = models.CharField(max_length=2, blank=True, null=True)
posicion_en_y = models.CharField(max_length=2, blank=True, null=True)
activo = models.BooleanField()
class Meta:
abstract = True
def save(self,*args,**kwargs):
self.total = self.cantidad_existente * self.precio_unitario
super().save(*args,**kwargs)
class Cortadores(Item):
tipo = models.ForeignKey(Tipos_Cortadores,on_delete=models.CASCADE)
material = models.ForeignKey(Materiales, on_delete=models.CASCADE)
filos = models.CharField(max_length=5, choices=GABILANES)
diametro = models.ForeignKey(Diametros, on_delete=models.CASCADE)
longitud = models.ForeignKey(Longitud, on_delete=models.CASCADE)
desbaste = models.CharField(max_length=1, choices=DESBASTE)
class Meta:
verbose_name_plural = "Cortadores"
def get_absolute_url(self):
return reverse('inventario:cortadores-list', kwargs={'id': self.id})
def __str__(self):
return '%s %s %s %s %s %s' % ( str(self.tipo), str(self.material), str(self.filos), str(self.diametro),
self.longitud, self.desbaste
)
The error is that you have two variables called total. Just rename one of them.
total = Cortadores.objects.filter(pk=pk).values('total')
...
new_value = nce * pu
total.objects.update(total=new_value)
There is a error, you changing you queryset to flaot value, and then trying to use queryset functions on it, so you should use other variable in total and not your own queryset
total = Cortadores.objects.filter(pk=pk).values('total')
total = nce * pu // This line replace your queryset to float
total.objects.update(total=total)
You should use something like that
qs_total = Cortadores.objects.filter(pk=pk).values('total')
total = calcular_nuevo_total(nce, pu)
for value in qs_total:
value.total=total
value.save()
To use it as generic like you want use pass the class reference as parameter and use it to call your query set
def something(__class__):
class.objects.all()
EDIT: Making a generic method to use with any class
from .models import Cortadores, OtherModelExample
def generic_update_noreturn(__class__, pk, total)
qs= __class__.objects.filter(pk=pk).values('total')
for value in qs:
value.total=total
value.save()
return qs
def generic_update_noreturn(__class__, pk, total)
__class__.objects.filter(pk=pk).values('total')update(total=total)
def actualizar_cantidad(request, pk):
...
total = calcular_nuevo_total(nce, pu)
updated_cortadores=generic_update(Cortadores, pk, total)
updated_otherexample=generic_update(OtherModelExample, total, pu)
generic_update_noreturn(Cortadores, pk, total)
(Sorry for my bad english)
I need to add a value to a object field, that i'm excluding from the formset. i like to auto assign this in a view. (i can't modify the model to add a def save method and make it there because is a third party app model)
This is the model
class Tax(models.Model):
"""A tax (type+amount) for a specific Receipt."""
tax_type = models.ForeignKey(
TaxType,
verbose_name=_('tax type'),
on_delete=models.PROTECT,
)
description = models.CharField(
_('description'),
max_length=80,
)
base_amount = models.DecimalField(
_('base amount'),
max_digits=15,
decimal_places=2,
)
aliquot = models.DecimalField(
_('aliquot'),
max_digits=5,
decimal_places=2,
)
amount = models.DecimalField(
_('amount'),
max_digits=15,
decimal_places=2,
)
receipt = models.ForeignKey(
Receipt,
related_name='taxes',
on_delete=models.PROTECT,
)
def compute_amount(self):
"""Auto-assign and return the total amount for this tax."""
self.amount = self.base_amount * self.aliquot / 100
return self.amount
class Meta:
verbose_name = _('tax')
verbose_name_plural = _('taxes')
This is the form and formset
class TaxForm(forms.ModelForm):
class Meta:
model = Tax
fields = [
'tax_type',
'description',
'base_amount',
'aliquot',
]
ReceiptTaxFormset = inlineformset_factory(
Receipt,
Tax,
form=TaxForm,
extra=0,
can_delete=False,
)
And this is the part of the view where i handle the formset
if form.is_valid() and entryFormset.is_valid() and taxFormset.is_valid():
receipt = form.save(commit=False)
# Tomamos el punto de venta de la sucursal y lo asignamos
pos = request.user.userprofile.branch_office.point_of_sales
receipt.point_of_sales = pos
receipt.document_number = client.dni_cuit
# Controlamos si el dni o cuit tiene 11 caracteres
# Si los tiene asigna CUIT al típo de documento
if len(client.dni_cuit) == 11:
document_type = DocumentType.objects.get(id=1)
receipt.document_type = document_type
else:
document_type = DocumentType.objects.get(id=10)
receipt.document_type = document_type
# Tomamos los valores de las lineas del comprobante
# y generamos los totales para asentar en el comprobante
total_amount = 0
for f in entryFormset:
cd = f.cleaned_data
qty = cd.get('quantity')
price = cd.get('unit_price')
vat = cd.get('vat')
subtotal = qty * price
total_amount = total_amount + subtotal
for t in taxFormset:
cd = t.cleaned_data
ba = cd.get('base_amount')
al = cd.get('aliquot')
ta = ba * al / 100
total_amount = total_amount + ta
# Asignamos el monto total a total_ammount
# y a net_taxed para factura tipo C ya que es igual
receipt.total_amount = total_amount
receipt.net_taxed = total_amount
# Asignamos 0 para Factura tipo C a los campos no necesarios
receipt.net_untaxed = 0
receipt.exempt_amount = 0
# Guardamos el comprobante y las lineas del mismo
receipt.save()
entryFormset.save()
taxFormset.save()
What i need to do is that in the taxFormset, for each tax that i get from the form, assign the amount to the object ta = ba * al / 100
Thanks!
inlineformset_factory sits on top of modelformset_factory which uses instances
I would love to see what error it's giving you or if it's not saving but maybe you are missing on something like this.
receipt.instance.total_amount = total_amount
receipt.instance.net_taxed = total_amount
# Asignamos 0 para Factura tipo C a los campos no necesarios
receipt.instance.net_untaxed = 0
receipt.instance.exempt_amount = 0
# Guardamos el comprobante y las lineas del mismo
receipt.save()
entryFormset.save()
taxFormset.save()
first im so sorry with my english, im new with Django, im from php + codeigniter.
I have a problem with a related_name, the tables are fine, but the problem is that in the Select (DropDown) the data display is "bad", in this way: Tarifas_object:
Html code:
<option value="1">Tarifas object</option>
how ill display the correct data?
my model:
class Tarifas(models.Model):
recarga = models.CharField(max_length=7, help_text='Codigo de la tarifa')
precio = models.DecimalField(max_digits=7, decimal_places=2)
diligencias = models.PositiveIntegerField(max_length=3)
def __inicode__(self):
return self.precio
class Meta:
verbose_name_plural="Tarifas"
class Recarga(models.Model):
socio = models.ForeignKey(User)
fecha = models.DateField(auto_now_add=True)
#valor = models.DecimalField(max_digits=6, decimal_places=2,verbose_name='Valor de la recarga', help_text= "Introduzca valores numericos ej.: 150.00")
valor = models.ForeignKey(Tarifas, related_name='recarga_valor')
diligencias = models.IntegerField(max_length=3, verbose_name='Cantidad de diligencias recargadas')
tiponcf = models.IntegerField(max_length=1,choices=TIPO_NCF, verbose_name='Tipo de comprobante fiscal')
ncf = models.CharField(max_length=19,verbose_name='Numero de comprobante fiscal')
cajero = models.CharField(max_length=20)
tipotarj = models.CharField(choices=TIPOS_TARJETAS, max_length=20, verbose_name='Tipo de tarjeta')
numtarj = models.IntegerField(max_length=16, verbose_name='Numero de tarjeta')
seguridad = models.IntegerField(max_length=3)
forma_pago = models.CharField(max_length=10, verbose_name='Forma de pago')
banco = models.CharField(max_length=20)
numerock = models.IntegerField(max_length=8, verbose_name='Numero de cheque')
def __unicode__(self):
return u'%s %s %s %s' % (self.socio,self.diligencias, self.fecha)
class Meta:
ordering = ['socio']
Thanks guys.
If that's the straight copy and paste from your model, you've misspelled unicode on the Tarifas model.