belek
belek

Reputation: 957

Django admin preview

I need to have "Preview" button in admin create/edit page. When user clicks on this button, form should be saved and redirects to specific url where user can see information about object he just added or edited.

So I have custom ModelAdmin class with needed for me inline formsets:

class InboundAdmin(admin.ModelAdmin, ListView):
    model = Inbound
    form = InboundForm
    change_form_template = 'admin/tour/inbound_form.html'
    inlines = [InboundTourDatesInline, InboundProgramInline, InboundFeedbackInline, InboundMedia,
               InboundTourSliderPhotoInline, InboundPriceDynamicsInline]

Template extends admin/change_form.html. This template has custom button:

<input type="submit" value="{% trans 'Preview' %}" class="default draft-submit" name="_save_as_draft" />

And this script which:

$('.draft-submit').on('click', function(){
                $.ajax({
                    type: "POST",
                    url: "{% url 'tours:inbound_draft' %}",
                    data: $("#{{ opts.model_name }}_form").serialize()
                });
            });

This is inbound_draft view:

def draft_inbounds(request):
    print('inbounds')
    form = InboundForm(request.POST or None, request.FILES or None)
    if form.is_valid():
        print('is_valid')
        form.save()
        # Here I should return specific url with pk as an attribute.
    print('not_valid')

Problem is that when I click on Preview button it saves changes but redirects me back to the list_view in admin. What is the right way to solve my problem?

Upvotes: 2

Views: 2472

Answers (2)

belek
belek

Reputation: 957

I have found a nice solution that completely solves my problem. All you need is to override save_model(), response_change() and response_add() methods of ModelAdmin class. Here is my class in admin.py file:

class InboundAdmin(admin.ModelAdmin, ListView):
    model = Inbound
    form = InboundForm
    change_form_template = 'admin/tour/inbound_form.html'
    inlines = [InboundTourDatesInline, InboundProgramInline, InboundFeedbackInline, InboundMedia,
               InboundTourSliderPhotoInline, InboundPriceDynamicsInline]

def save_model(self, request, obj, form, change):
    if "_draft" in request.POST:
        obj.published = False  # If it is a draft, then post should not be published
    else:
        obj.published = True
    super(InboundAdmin, self).save_model(request, obj, form, change)

def response_change(self, request, obj):
    if "_draft" in request.POST:
        return HttpResponseRedirect(reverse_lazy("tours:inbound_detail", kwargs={'pk': obj.pk}))
    else:
        return super(InboundAdmin, self).response_change(request, obj)

def response_add(self, request, obj, post_url_continue=None):
    if "_draft" in request.POST:
        return HttpResponseRedirect(reverse_lazy("tours:inbound_detail", kwargs={'pk': obj.pk}))
    else:
        return super(InboundAdmin, self).response_add(request, obj, post_url_continue=None)

If request contains "_draft" then I redirect to needed url, else It uses default method.

In your template you should add button for savins as a draft:

<input type="submit" value="{% trans 'Save as a draft' %}" class="default draft-submit" name="_draft" />

That's all)

Upvotes: 2

Alex Morozov
Alex Morozov

Reputation: 5993

As you made your button the submit input, it submits the form, therefore redirecting you to the changelist. To make that button "js-only", change its type:

<input type="button" value="{% trans 'Preview' %}" class="default draft-submit" name="_save_as_draft" />

Alternatively you could prevent the form submission via JS:

$('.draft-submit').on('click', function(event){
    event.preventDefault();
    // the rest of your code
});

Upvotes: 1

Related Questions