#14071: Row duplicated when modifying PK -------------------------------+-------------------------------------- Reporter: mnbayazit | Owner: nobody Type: Bug | Status: new Component: contrib.admin | Version: 1.2 Severity: Normal | Resolution: Keywords: | Triage Stage: Unreviewed Has patch: 0 | Needs documentation: 0 Needs tests: 0 | Patch needs improvement: 0 Easy pickings: 0 | UI/UX: 0 -------------------------------+-------------------------------------- Changes (by jedie):
* status: closed => new * severity: => Normal * resolution: wontfix => * easy: => 0 * ui_ux: => 0 * type: => Bug Comment: I just had to deal with the original Problem: ''Row duplicated when modifying PK'' By changing the primary key in admin you can also get another problem: If the model has another unique field, then you get the admin error that the field value is not unique. (This is because django tries to create a duplicated entry). The problem is: {{{django.forms.models.BaseModelForm._post_clean()}}} will do this: {{{ self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) }}} And {{{construct_instance()}}} will just change the primary key and this object will be saved in the end and results in duplication. This is a known behaviour as the docs says at: https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.Field.primary_key If you change the value of the primary key on an existing object and then save it, a new object will be created alongside the old one. One way to change the primary key is: {{{ FooBarModel.objects.filter(id=old_id).update(id=new_id) }}} My current work-a-round it to use a own model form: {{{ class FooBarAdminForm(forms.ModelForm): def clean_id(self): old_id = self.instance.id new_id = self.cleaned_data['id'] if old_id == new_id: # ID not changed -> nothing to do return old_id if FooBarModel.objects.filter(id=new_id).exists(): raise forms.ValidationError('ID already exists') # Change the primary key without make a new object: FooBarModel.objects.filter(id=old_id).update(id=new_id) # replace new created instance, made in self._post_clean() via construct_instance(): self.instance = FooBarModel.objects.get(id=new_id) return new_id }}} But that's not a perfect solution: * It's not universal. (Primary key must be {{{id}}}) * Has potential for race-contitions * Admin ''continue'' will redirect to {{{request.path}}} that will contain the old ID (But can be fixed via overwriting {{{response_change()}}}) -- Ticket URL: <https://code.djangoproject.com/ticket/14071#comment:2> Django <https://code.djangoproject.com/> The Web framework for perfectionists with deadlines. -- You received this message because you are subscribed to the Google Groups "Django updates" group. To unsubscribe from this group and stop receiving emails from it, send an email to django-updates+unsubscr...@googlegroups.com. To view this discussion on the web visit https://groups.google.com/d/msgid/django-updates/067.e4ca740028a4bc5bcfc56040537755e7%40djangoproject.com.