Reputation: 37474
I've got a Position
model with a ForeignKey field related to an Emplacement
model.
In the admin of Emplacement
I set an Inline admin for Position
.
What I want to achieve is that when the num_position
field is modified in a Emplacement
instance, the positions related to this instance are deleted and new ones are created.
To achieve this I override the Emplacement
model's save()
method and check whether num_position
has changed.
The problem is that when saving an Emplacement
model after modification of its num_position
field, I get the following error:
ValidationError: Select a valid choice. That choice is not one of the available choices.
I guess it has to do with the deletion/recreation of the positions, but I can't find why.
What's wrong?
models.py
class Emplacement(models.Model):
num_position = models.IntegerField()
# more fields
__original_num_position = None
def __init__(self, *args, **kwargs):
super(Emplacement, self).__init__(*args, **kwargs)
self.__original_num_position = self.num_position
def save(self, *args, **kwargs):
if self.num_position != self._Emplacement__original_num_position:
# if num_position has changed, delete existing positions and recreate <num_position> positions
Position.objects.filter(emplacement=self).delete()
for i in range(self.num_position):
position = Position()
position.emplacement = self
position.number = i+1
position.save()
super(Emplacement, self).save(*args, **kwargs)
class Position(models.Model):
emplacement = models.ForeignKey(Emplacement)
number = models.IntegerField()
admin.py
class PositionInline(admin.TabularInline):
model = Position
class EmplacementAdmin(admin.ModelAdmin):
inlines = [PositionInline]
my_site.register(Emplacement, EmplacementAdmin)
EDIT
I also tried with a signal, but got the same error:
@receiver(post_save, sender=Emplacement)
def create_positions(sender, instance, created, **kwargs):
"""Create positions when num_position has changed."""
if instance.num_position != instance.old_num_position:
Position.objects.filter(emplacement=instance).delete()
for i in range(instance.num_position):
position = Position()
position.emplacement = instance
position.numero = i+1
position.save()
Upvotes: 0
Views: 444
Reputation: 81
I encountered exactly the same issue. Finally solved it by not implementing the create/delete logic on the Model-side but in the Forms/FormSets.
So in the 'main' admin class, in your case 'EmplacementAdmin', extended the save_formset method:
def save_formset(self, request, form, formset, change):
instance = form.instance
deleted = []
if instance and instance.__original_num_position != instance.num_position:
''' create new Positions if needed '''
formset.delete_positions = None # pass on a list, QuerySet, whatever
super(EmplacementAdmin, self).save_formset(request, form, formset, change)
And create a PositionFormSet like so:
class PositionFormSet(BaseInlineFormSet):
delete_positions = []
@property
def deleted_forms(self):
deleted_forms = []
try:
deleted_forms = super(PositionFormSet, self).deleted_forms
except AttributeError:
pass
for form in self.forms:
if form.instance in self.delete_positions:
deleted_forms.append(form)
self.delete_positions = []
return deleted_forms
And set this formset in your inlineadmin:
class PositionInline(admin.TabularInline):
model = Position
formset = PositionFormSet
Not exactly the way I had in mind, but the trick worked for me. Curious to know if there are some more elegant solutions :-)
Upvotes: 1