J'ai un modèle simple maître/détail de Citation (Master) contenant les informations d'en-tête et les totaux, et QuotationDetail (détail) contenant les articles de devis, ce modèle enfant a un champ PriceLevel FK utilisé pour calculer le balisage pour chaque article individuel. Je suis en train de mettre en œuvre une action d'administration de Django si l'administrateur peut modifier ce pricelevel sur plusieurs éléments à la fois, mais je reçois l'erreur:Python Django 1.11 "ValueError Format incomplet" dans l'action admin lorsque vous essayez de mettre à jour le terrain FK
[myProjectPath]\lib\site-packages\django\contrib\admin\options.py", line 812, in get_action_choices
choice = (name, description % model_format_dict(self.opts))
ValueError: incomplete format
Ceci est mon models.py (sur les modèles concernés):
class NivelDePrecio(models.Model): # PriceLevel
# Fields
nombre = models.CharField(max_length=50)
slug = extension_fields.AutoSlugField(populate_from='nombre', blank=True)
valor = models.DecimalField(max_digits=7, decimal_places=4)
factor = models.DecimalField(max_digits=7, decimal_places=6, null=True, blank=True)
class Meta:
ordering = ('-valor',)
def __str__(self):
return self.nombre
def save(self, *args, **kwargs):
self.factor = 1/(1 - self.valor)
super(NivelDePrecio, self).save()
class Cotizacion(models.Model): # Quotation (master)
# Fields
nombre = models.CharField(max_length=100)
slug = extension_fields.AutoSlugField(populate_from='nombre', blank=True, overwrite=True)
fecha_ida = models.DateField(default=date.today)
fecha_regreso = models.DateField(default=date.today)
# Relationship Fields
itinerario = models.ForeignKey(Itinerario, on_delete=CASCADE, verbose_name='itinerario')
nivel_de_precio = models.ForeignKey(NivelDePrecio, verbose_name='nivel de precio',
on_delete=models.PROTECT, null=True, blank=True)
class Meta:
ordering = ('itinerario__cliente__codigo', '-fecha_ida')
def __str__(self):
return str(self.nombre)
class CotizacionDetalle(models.Model): # QuotationDetail (detail)
# Fields
descripcion = models.CharField(max_length=100, null=True, blank=True)
cantidad = models.DecimalField(max_digits=10, decimal_places=2)
costo = models.DecimalField(max_digits=10, decimal_places=2, default=0, editable=False)
monto = models.DecimalField(max_digits=10, decimal_places=2, default=0, editable=False)
markup = models.DecimalField(max_digits=6, decimal_places=4, default=0, editable=False)
utilidad = models.DecimalField(max_digits=6, decimal_places=4, default=0, editable=False)
total = models.DecimalField(max_digits=10, decimal_places=2, default=0, editable=False)
slug = extension_fields.AutoSlugField(populate_from='item', blank=True)
# Relationship Fields
cotizacion = models.ForeignKey(Cotizacion, on_delete=CASCADE, verbose_name='cotizacion', related_name='lineas')
item = models.ForeignKey(Item, verbose_name='item', related_name='item')
nivel_de_precio = models.ForeignKey(NivelDePrecio, verbose_name='nivel de precio',
on_delete=models.PROTECT, null=True, blank=True)
class Meta:
ordering = ('id',)
def save(self, *args, **kwargs):
self.descripcion = self.item.descripcion_venta
self.costo = self.item.costo
self.monto = self.cantidad * self.costo
if self.nivel_de_precio is None:
self.nivel_de_precio = self.cotizacion.nivel_de_precio
self.markup = Decimal(round(self.nivel_de_precio.factor - 1, 4)).quantize(Decimal("0.0000"))
self.utilidad = Decimal(self.nivel_de_precio.valor).quantize(Decimal("0.0000"))
self.total = Decimal(self.monto) * (1 + Decimal(self.markup))
super(CotizacionDetalle, self).save(*args, **kwargs)
def __str__(self):
return str(self.descripcion)
Ceci est mon admin.py: (J'ai aussi essayé la version commentée de l'action avec la même erreur résultant). Ce serait bien de pouvoir choisir des options pour ce changement du modèle FK, mais je vais me contenter de le faire en ayant deux actions: une pour augmenter la valeur de balisage de .05% et une autre pour diminuer la marge de .05% dans dans quels cas j'aurais besoin de mettre à jour le champ de balisage à la place.
class CotizacionDetalleAdminForm(forms.ModelForm):
class Meta:
model = CotizacionDetalle
fields = ['item', 'cotizacion', 'cantidad', 'nivel_de_precio']
def cambiar_utilidad(modeladmin, request, queryset):
from .models import NivelDePrecio
ndp = NivelDePrecio.objects.get(id=5)
queryset.update(nivel_de_precio_id=ndp.id)
# for cd in queryset:
# cd.nivel_de_precio_id = ndp.id
# cd.save()
cambiar_utilidad.short_description = "Cambiar Utilidad 25%"
class CotizacionDetalleAdmin(admin.ModelAdmin):
# save_as = True
form = CotizacionDetalleAdminForm
list_display = ['cliente', 'itinerario', 'cotizacion', 'descripcion',
'cantidad', 'costo', 'monto', 'utilidad', 'markup', 'total']
list_display_links = ['descripcion']
readonly_fields = ['descripcion', 'costo', 'monto', 'utilidad', 'markup', 'total', 'slug', 'creado', 'actualizado']
search_fields = ['descripcion']
list_filter = (('cotizacion__itinerario__cliente', DropdownFilterRelated),
('cotizacion__itinerario', DropdownFilterRelated),
('cotizacion', DropdownFilterRelated),)
ordering = ['cotizacion__itinerario__cliente__codigo', 'cotizacion__fecha_ida', 'id']
actions = [cambiar_utilidad]
admin.site.register(CotizacionDetalle, CotizacionDetalleAdmin)
Mise à jour:
... essayé ceci:
https://djangosnippets.org/snippets/1836/
comme suit dans mon admin.py
def create_action_nivel(nivel):
def action(modeladmin, request, queryset):
queryset.update(nivel_de_precio=nivel)
name = "change_to_%s" % (nivel.slug,)
return (name, (action, name, "Cambiar Nivel de Precio a: %s" % (nivel,)))
...
class CotizacionDetalleAdmin(admin.ModelAdmin):
def get_actions(self, request):
return dict(create_action_nivel(n) for n in NivelDePrecio.objects.all())
Mais je reçois la même erreur, retraçage montré ici: http://dpaste.com/360GEJY
Le même extrait fonctionne très bien lorsque le champ de mise à jour n'est pas un champ de clé étrangère . Je l'ai utilisé dans un autre modèle et fonctionne très bien:
def create_action_estatus(estatus):
def action(modeladmin, request, queryset):
queryset.update(estatus=estatus)
name = "mark_%s" % (estatus,)
return name, (action, name, "Cambiar Estatus: %s " % (estatus,))
class ItinerarioAdmin(admin.ModelAdmin):
def get_actions(self, request):
statuses= [ "Solicitado", "Cotizado", "Confirmado", "Facturado", "Cerrado"]
return dict(create_action_estatus(e) for e in statuses)