Opened 14 years ago

Closed 5 years ago

#14071 closed Bug (wontfix)

Row duplicated when modifying PK

Reported by: mnbayazit Owned by: nobody
Component: contrib.admin Version: 1.2
Severity: Normal Keywords:
Cc: jedie Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

Create a model and add a field with primary_key=True
Go into the django admin panel and change this primary key.
A new entry is added with the new PK, but the old one remains as well.

Sample model:

class FlatPage(Model):

title = CharField(max_length=50)
key = SlugField(max_length=50, primary_key=True, help_text="Do not change.")
content = TextField()
created = DateTimeField(auto_now_add=True)
updated = DateTimeField(auto_now=True)
def unicode(self):

return self.title

class Meta:

app_label = 'app'

Change History (4)

in reply to:  description comment:1 by Chris Beaven, 14 years ago

Resolution: wontfix
Status: newclosed

Replying to mnbayazit:

Create a model and add a field with primary_key=True
Go into the django admin panel and change this primary key.
A new entry is added with the new PK, but the old one remains as well.

Sample model:

class FlatPage(Model):
    title = CharField(max_length=50)
    key = SlugField(max_length=50, primary_key=True, help_text="Do not change.")
    content = TextField()
    created = DateTimeField(auto_now_add=True)
    updated = DateTimeField(auto_now=True)
    def __unicode__(self):
	return self.title
    class Meta:
	app_label = 'app'

The primary key is exactly how the decision is made as to whether update or create a new row.

Altering this behavior would be a fundamentally backwards incompatible change.

comment:2 by jedie, 5 years ago

Easy pickings: unset
Resolution: wontfix
Severity: Normal
Status: closednew
Type: Bug
UI/UX: unset

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())

EDIT: This is also a little related to: https://code.djangoproject.com/ticket/2259 rimary keys should be readonly by default in admin

Version 1, edited 5 years ago by jedie (previous) (next) (diff)

comment:3 by jedie, 5 years ago

Cc: jedie added

comment:4 by Mariusz Felisiak, 5 years ago

Resolution: wontfix
Status: newclosed
Note: See TracTickets for help on using tickets.
Back to Top