I’m starting to a bit frustrated and could really use some direction on this. My site is centered around a sheet music catalog. So each piece of sheet music will have one or more sample images to go with it. For my sheet admin view, I have a single sheet editor with a stacked inline for sample pages. That part is working fine. What I’m trying to add is a way of saying, adding a checkbox to generate a custom cover and then when I save part of the save method runs a script to build the image then saves it as another record in the database. My first thought was a custom save() method in my sheet admin form but I don’t see how to reference the images from the save method. Then I thought maybe looking at adding the save to the inline form but then I can’t reference the parent data. Can someone give a few ideas of where to go with this?
From your explanation I would add the methods to the Model. Both the generation of the album art and also the save method. Though I would pass the generation as a argument to the save method and call the generation method while processing the save.
If you could provide some code it would be easier to help you.
Thanks for taking a look at this. I also realized a wrinkle to all this because during the saving of the samples, I need access to the composer inline as well to be able to generate the composer. Let me write this out so hopefully I’m a bit clearer.
Somewhere during the save/udate process for each sheet of music the following is done:
- Check if the update_cover BooleanField is checked.
- If it is true, pull the title from the sheet form (model?) as well as the composers from the composer inline form.
- Create a custom cover that includes the title and a formatted artist string (this part I have worked out)
- Insert the new created cover in with the rest of the sample art that is generated from the SampleInline.
I tried to cut down the code as much as possible removing lots of excess fields such as publisher and genre so its hopefully a bit manageable:
## models.py class Composer(models.Model): name_text = models.CharField(max_length=150, unique=True) #sheets = models.ManyToManyField(Sheet) def __str__(self): return self.name_text class Meta: ordering = ['name_text'] class Sheet(models.Model): title_text = models.CharField(max_length=200, blank=False) composers = models.ManyToManyField(Composer) class SampleImage(models.Model): sheet = models.ForeignKey(Sheet, related_name = "samples", on_delete=models.CASCADE, null=True) sample_type = models.IntegerField(choices=IMAGE_CHOICES, default=IMAGE_CHOICES[-1][0]) sample_image = models.ImageField(upload_to=sample_image_upload_to) order = models.IntegerField(blank=True, null=True) def display_sample_image(self): width = 200 height = width * self.sample_image.height / self.sample_image.width return format_html('<img src="' + self.sample_image.url + '" width=' + str(width) + ' height=' + str(height) + ">") display_sample_image.short_description = "Image" class Meta: ordering = ('order',) def __str__(self): return dict(IMAGE_CHOICES)[self.sample_type] + " for " + self.sheet.title_text #admin.py from django.contrib import admin from django import forms from django.utils.html import format_html from django.urls import reverse from .models import Sheet, Composer, SampleImage class ComposerInline(admin.StackedInline): model = Sheet.composers.through extra = 2 verbose_name = "Composer" verbose_name_plural = "Composers" class SampleInline(admin.StackedInline): model = SampleImage extra = 3 verbose_name = "Sample Page" verbose_name_plural = "Sample Pages" fieldsets = ( (None, { 'fields' : ("sample_type", "sample_image", "order", "display_sample_image",) }), ) readonly_fields = ("display_sample_image",) def display_sample_image(self, obj): return obj.display_sample_image() display_sample_image.short_description = "Display" class SheetAdminForm(forms.ModelForm): model = Sheet title_text = forms.CharField(widget=forms.Textarea, required=True) description_text = forms.CharField(widget=forms.Textarea, required=False) update_cover = forms.BooleanField(required=False) class SheetAdmin(admin.ModelAdmin): list_display = ('title_text', 'get_composers') ordering = ('title_text',) fieldsets = [ (None, {'fields' : ['title_text']}), ('Cover Generator', {'fields' : ['update_cover', ('override_title', 'updated_title'), ('override_composers', 'updated_composers'), ('override_level', 'updated_level'), ('override_instrumentation', 'updated_instrumentation'), ('override_intproperty', 'updated_intproperty'), ('override_collection', 'updated_collection'),]}) ] inlines = [ComposerInline, SampleInline] exclude = ('composer', 'samples') form = SheetAdminForm # Register your models here. admin.site.register(Sheet, SheetAdmin) admin.site.register(Composer) admin.site.register(SampleImage)
I think I’m with you up to here. I see that if I override save() in the model, my model is already filled in with everything I need so all I need to do is get all the parts I need to pass to the art generator (title, composers, etc). So now I need to have in the save whether or not a cover was requested or in other words the part where you say pass the generation as a argument – where might I do that?
Ok I think I have it worked out. Here is what I have:
class SheetAdmin(admin.ModelAdmin):
## Snipped ##
def save_model(self, request, obj, form, change):
obj.update_cover = form.cleaned_data.get("update_cover")
super().save_model(request, obj, form, change)
then...
class Sheet(models.Model):
## Snipped ##
def save(self, *args, **kwargs):
if self.update_cover:
print("Yep lets make a cover....")
super().save(*args, **kwargs)
I think I can puzzle out the rest. I’ll be sure to follow up if I have trouble. Thanks again!
I was just about to write this - also I was thinking about the save method in save_model
you could also add an argument like
super().save_model(request, obj, form, change, update_cover=should_update_cover)
and the use it in the save
method:
def save(self, update_cover=False, *args, **kwargs):
if update_cover:
[...]
Oh so thats how I can pass keywords and arguments. When I tried it on a different save method I got an error. That works too.
Thanks!
Joel