In Django, how to override the 'Save and continue' feature? - django

I need to add some pre- and post-save logic to my ModelAdmin, but only when the user submitted the form via the 'Save and continue editing' button and not the 'Save' button. How can I do this?

Just like overriding the normal save method, you need to override the save_model() function in your ModelAdmin, which includes the request object. From the request object you can get the POST object, which will include a '_continue' key if the user clicked the 'Save and continue button'. Example:
class MyAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, changed):
if '_continue' in request.POST:
# add your code here
return super(ServerAdmin, self).change_view(request, obj, form, changed)

Related

How to add custom action to Django Admin Save button

I'm trying to add a custom action to the save button in the admin interface. Basically, when the user changes the form and hits the save button, I don't want to change the default save action but I want to add custom functionality. Like; getting some or all data from the form and calling an endpoint or calling an other function.
User saves -> Default save action happens -> *Custom action happens
I'm aware that I can overwrite save_model of ModelAdmin with:
class MyAdminView(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
super().save_model(request, obj, form, change)
# add custom feature <--
but I'm not really sure if this is the appropriate way to do this

Django admin save() doesn't get data from ManyToManyField

I'm trying to get chosen objects from affiliate_networks in Django admin when the user clicks the submit button.
When I choose one from affiliate_networks and submit it, the console prints an empty of affiliate_networks, and then I come back to the page and the chosen object is stored properly. So, I submit it again, then the console prints the chosen object. save() only receives objects that are already stored, not objects that I choose before saving.
Is there a way, that I can have save() to notice affiliate_networks to have any object chosen?
class Store(models.Model):
...
affiliate_networks = models.ManyToManyField(AffiliateNetwork, blank=True)
def save(self, *args, **kwargs):
print(self.affiliate_networks.all())
You can't do it in save() - as you have discovered, Django admin doesn't save ManyToMany objects until afterwards. You need to do it in the save_related method of ModelAdmin. See https://timonweb.com/posts/many-to-many-field-save-method-and-the-django-admin/
In admin.py:
...
class StoreAdmin(admin.ModelAdmin):
def save_related(self, request, form, formsets, change):
super(StoreAdmin, self).save_related(request, form, formsets, change)
print(form.instance.affiliate_networks.all())
...
admin.site.register(Store, StoreAdmin)

Display custom message from signal in the admin

I have a pre-save signal listener that updates a second model. The same as this example:
Django Signals to update a different model
I'd like to let the user know that the listener succeeded in updating the model and provide some information. Normally, I would think I could use the built in messages functionality that django has. The problem is that the signal doesn't have access to 'request'. So I can't see how to use the built in Django Messages Framework.
https://docs.djangoproject.com/en/dev/ref/contrib/messages/
Is there a known method for sending a message to the user in the admin? Maybe by overriding the save() method for one of the models? (the one sending the signal, or receiving), but I don't think the save() method has access to 'request' either?
This must be something others want to do as well?
You can override save_model method in ModelAdmin. Something like this:
from django.contrib import messages
# your imports
...
# your code
def save_model(self, request, obj, form, change):
obj.user = request.user
obj.save()
# you can just call super(YourModelAdminName, self).save_model(request, obj, form, change)
messages.add_message(request, messages.INFO, 'Text of message')

How to implement a normal model admin like User model in django admin site

As we know, After we add a new user in django admin site, page is redirected to editing profile, not user list page like other models. How to implement this in a normal model? For example I defined a model called Image and wanted to edit the image after the image had been uploaded.
Put the following function into your admin class.
django/contrib/auth/admin.py:L139
def response_add(self, request, obj, post_url_continue='../%s/'):
"""
Determines the HttpResponse for the add_view stage. It mostly defers to
its superclass implementation but is customized because the User model
has a slightly different workflow.
"""
# We should allow further modification of the user just added i.e. the
# 'Save' button should behave like the 'Save and continue editing'
# button except in two scenarios:
# * The user has pressed the 'Save and add another' button
# * We are adding a user in a popup
if '_addanother' not in request.POST and '_popup' not in request.POST:
request.POST['_continue'] = 1
return super(UserAdmin, self).response_add(request, obj, post_url_continue)

Django: customizing the message after a successful form save

whenever I save a model in my Admin interface, it displays the usual "successfully saved message."
However, I want to know if it's possible to customize this message because I have a situation where I want to warn the user about what he just saved and the implications of these actions.
class PlanInlineFormset(forms.models.BaseInlineFormset):
def clean(self):
### How can I detect the changes?
### (self.changed_data doesn't work because it's an inline)
### and display what he/she just changed at the top AFTER the successful save?
class PlanInline(admin.TabularInline):
model = Plan
formset = PlanInlineFormset
Django (> version 1.2) uses the messages framework for admin messages. You can add additional messages using that interface. Here's an example:
from django.contrib import messages
class SomeModelAdmin(admin.ModelAdmin):
# your normal ModelAdmin stuff goes here
def save_model(self, request, obj, form, change):
# add an additional message
messages.info(request, "Extra message here.")
super(SomeModelAdmin, self).save_model(request, obj, form, change)
To detect changes to the object being saved, you should be to override the save_model method of ModelAdmin, and compare the object the method is passed to the version currently in the database. To do this in the case of inlines, you can override the save_formset method. A possible approach might look like (untested code):
class SomeModelAdmin(admin.ModelAdmin):
# your normal ModelAdmin stuff goes here
def save_formset(self, request, form, formset, change):
if not change:
formset.save()
else:
instances = formset.save(commit=False)
for instance in instances:
try:
# if you've got multiple types of inlines
# make sure your fetching from the
# appropriate model type here
old_object = SomeOtherModel.get(id=instance.id)
except SomeOtherModel.DoesNotExist:
continue
if instance.field_x != old_object.field_x:
messages.info(request, "Something Changed")
instance.save()
formset.save_m2m()
If you're using Django 1.2 or newer, the messages framework may hold the answer.
http://docs.djangoproject.com/en/dev/ref/contrib/messages/