Automatically backing up the last entry to a field in django admin
I have a model that serves as a price database for a part:
class Part(models.Model):
name = models.CharField("name", max_length=128)
class Price(models.Model):
value = models.DecimalField(max_digits=10, decimal_places=2)
part = models.ForeignKey(Part, on_delete=models.CASCADE)
is_active = models.BooleanField(default=True)
in the admin I want to have a field that can change the price, but also a list where the old prices are visible. I kind of solved it with Django built in methods, but it seems rather complicated for such a simple request:
class FakePlainTextWidget(forms.Widget):
"""
this widget masks an input field for prices so they are only being listed and not really editable or look that way
"""
def __init__(self, attrs=None):
super().__init__(attrs)
def render(self, name, value, attrs, renderer=None):
s = f"""border: none; background-color: transparent; pointer-events: none; -webkit-appearance: none; -moz-appearance: textfield;"""
return format_html(f"""<input type="number" name="{attrs["id"][3:]}" value="{value}" id="{attrs["id"]}" style="{s}"> €""")
class PriceInlineForm(forms.ModelForm):
class Meta:
model = Price
fields = "__all__"
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if self.instance and not self.instance.is_active:
# make fields read-only if price is_active=False
for field in self.fields:
self.fields[field].widget = FakePlainTextWidget()
def save(self, commit=False):
instance = super().save(commit=False)
instance.is_active = False
instance.save(update_fields=["is_active"])
Price.objects.create(value=self.cleaned_data["value"], part=instance.part)
return instance
class PriceInline(admin.TabularInline):
model = Price
can_delete = False
exclude = ["is_active",]
extra = 0 # No extra empty forms
min_num = 1 # Ensure at least one form is displayed
max_num = 1 # Ensure only one form can be added
form = PriceInlineForm
def get_queryset(self, request):
qs = super().get_queryset(request)
return qs.order_by("-id") # so active input is first
@admin.register(Part)
class PartAdmin(admin.ModelAdmin):
inlines = [PriceInline]
I feel like there should be a much simpler way to achieve the same?