Reputation: 378
I am updating a django app from 1.4 to 1.8 and have hit a small problem with django admin.
My models look like this
def new_key():
return binascii.b2a_hex(os.urandom(20))
class ApiKey(models.Model):
customer = models.ForeignKey(UserProfile)
key = models.CharField(max_length=40, default=new_key)
And admin.py is
class ApiKeyInline(admin.StackedInline):
model = ApiKey
extra = 0
class UserProfileAdmin(admin.ModelAdmin):
inlines = [ApiKeyInline]
admin.site.register(UserProfile, UserProfileAdmin)
When using admin page api key get correctly populated with a random value. However when saving the UserProfile it doesn't get saved, it is as if nothing was added. If I manually change a a single leter in the autogenerated key saving works correctly. It seems to me this is a problem with django detecting a change or something like this.
Any suggestions? Code worked in 1.4.
Upvotes: 2
Views: 2077
Reputation: 186
There is a solution in one of the old threads: How to force-save an "empty"/unchanged django admin inline?
You should mark your inline form as always changed.
from django.forms import ModelForm
from .models import UserProfile
class AlwaysChangedModelForm(ModelForm):
def has_changed(self):
""" Should returns True if data differs from initial.
By always returning true even unchanged inlines will get validated and saved."""
return True
#An inline model
class ApiKey(admin.StackedInline):
model = ApiKey
extra = 0
form = AlwaysChangedModelForm
Upvotes: 4
Reputation: 2335
A short investigation lead me to the save_new_objects
of class BaseModelFormSet
, located in django/forms/models.py
.
It has the following check: if not form.has_changed()
Looks promising, huh? Now, we want to "enhance" this method. Where to start? Well... Inlines inherit from InlineModelAdmin
, which has an get_formset
method. So...
class ApiKeyInline(admin.StackedInline):
model = ApiKey
extra = 0
def get_formset(self, *args, **kwargs):
formset = super(EmailInlineAdmin, self).get_formset(*args, **kwargs)
# at this point, formset is a generated class called like "ApiKeyFormSet"
# as it is a regular python objects, no one stops us from playing with it
formset.save_new_objects = ApiKeyInline.my_own_save_new_objects_method
return formset
def my_own_save_new_objects_method(self, commit=True):
# here should be something like
# django/forms/models.py BaseModelFormSet.save_new_objects
return self.new_objects
The contents of my_own_save_new_objects_method
you have to edit yourself. The original method, once again, is in dist-packages/django/forms/models.py
, either call it via super or write something entirely yours, anyways skip the check.
Also, maybe it is a terrible and overcomplicated solution. I have a feeling that there should be a better one. In example, not setting default=new_key
in your model, but setting a default value for the form field, in example.
Upvotes: 1