-
Notifications
You must be signed in to change notification settings - Fork 490
Feature: add custom object id field #999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 10 commits
232f810
fe504ee
6dbd423
31151f9
80a116f
eedcbef
9d0936f
d3fbf84
e13bb70
d5c51ae
b1e3f67
d27560a
7d81001
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,7 +18,7 @@ | |
|
|
||
| from reversion.errors import RevertError | ||
| from reversion.models import Version | ||
| from reversion.revisions import is_active, register, is_registered, set_comment, create_revision, set_user | ||
| from reversion.revisions import is_active, register, is_registered, set_comment, create_revision, set_user, _get_options | ||
| from reversion.utils import mute_signals | ||
|
|
||
|
|
||
|
|
@@ -200,7 +200,13 @@ def _reversion_revisionform_view(self, request, version, template_name, extra_co | |
| version.revision.revert(delete=True) | ||
| # Run the normal changeform view. | ||
| with self.create_revision(request): | ||
| response = self.changeform_view(request, quote(version.object_id), request.path, extra_context) | ||
| obj = get_object_or_404( | ||
| version._model._default_manager.using(version.db), | ||
| **{_get_options(version._model).object_id_field: version.object_id}, | ||
| ) | ||
| response = self.changeform_view( | ||
| request, quote(str(obj.pk)), request.path, extra_context | ||
| ) | ||
| # Decide on whether the keep the changes. | ||
| if request.method == "POST" and response.status_code == 302: | ||
| set_comment(_("Reverted to previous version, saved on %(datetime)s") % { | ||
|
|
@@ -305,6 +311,8 @@ def history_view(self, request, object_id, extra_context=None): | |
| if not self.has_change_permission(request): | ||
| raise PermissionDenied | ||
|
|
||
| obj = get_object_or_404(self.model, pk=unquote(object_id)) | ||
| reversion_object_id = str(getattr(obj, _get_options(self.model).object_id_field)) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As above, this is worth optimizing for the common case of |
||
| opts = self.model._meta | ||
| action_list = [ | ||
| { | ||
|
|
@@ -317,7 +325,7 @@ def history_view(self, request, object_id, extra_context=None): | |
| for version | ||
| in self._reversion_order_version_queryset(request, Version.objects.get_for_object_reference( | ||
| self.model, | ||
| unquote(object_id), # Underscores in primary key get quoted to "_5F" | ||
| reversion_object_id, | ||
| ).select_related("revision", "revision__user")) | ||
| ] | ||
| # Compile the context. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ | |
| "for_concrete_model", | ||
| "ignore_duplicates", | ||
| "use_natural_foreign_keys", | ||
| "object_id_field" | ||
| )) | ||
|
|
||
|
|
||
|
|
@@ -168,7 +169,7 @@ def _add_to_revision(obj, using, model_db, explicit): | |
| return | ||
| version_options = _get_options(obj.__class__) | ||
| content_type = _get_content_type(obj.__class__, using) | ||
| object_id = force_str(obj.pk) | ||
| object_id = force_str(getattr(obj, version_options.object_id_field)) | ||
| version_key = (content_type, object_id) | ||
| # If the obj is already in the revision, stop now. | ||
| db_versions = _current_frame().db_versions | ||
|
|
@@ -191,7 +192,9 @@ def _add_to_revision(obj, using, model_db, explicit): | |
| ) | ||
| # If the version is a duplicate, stop now. | ||
| if version_options.ignore_duplicates and explicit: | ||
| previous_version = Version.objects.using(using).get_for_object(obj, model_db=model_db).first() | ||
| previous_version = Version.objects.using(using).get_for_object_reference( | ||
| obj.__class__, object_id, model_db=model_db | ||
| ).first() | ||
| if previous_version and previous_version._local_field_dict == version._local_field_dict: | ||
| return | ||
| # Store the version. | ||
|
|
@@ -209,6 +212,11 @@ def add_to_revision(obj, model_db=None): | |
| _add_to_revision(obj, db, model_db, True) | ||
|
|
||
|
|
||
| def _get_object_id_field(model): | ||
|
etianen marked this conversation as resolved.
Outdated
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are some parts of the codebase using What if... we resolved So make This means explicitly setting
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. resolving object_id_field here using ._meta.pk.name resulted in errors, for models with multi-table inheritence, so instead i used meta.pk.attname. |
||
| field = _get_options(model).object_id_field | ||
| return model._meta.pk.name if field == "pk" else field | ||
|
|
||
|
|
||
| def _save_revision(versions, user=None, comment="", meta=(), date_created=None, using=None): | ||
| from reversion.models import Revision | ||
| from reversion.models import Version | ||
|
|
@@ -221,7 +229,9 @@ def _save_revision(versions, user=None, comment="", meta=(), date_created=None, | |
| model: { | ||
| db: frozenset(map( | ||
| force_str, | ||
| model._base_manager.using(db).filter(pk__in=pks).values_list("pk", flat=True), | ||
| model._base_manager.using(db).filter( | ||
| **{f"{_get_object_id_field(model)}__in": pks} | ||
| ).values_list(_get_object_id_field(model), flat=True), | ||
| )) | ||
| for db, pks in db_pks.items() | ||
| } | ||
|
|
@@ -376,7 +386,7 @@ def _get_senders_and_signals(model): | |
|
|
||
|
|
||
| def register(model=None, fields=None, exclude=(), follow=(), format="json", | ||
| for_concrete_model=True, ignore_duplicates=False, use_natural_foreign_keys=False): | ||
| for_concrete_model=True, ignore_duplicates=False, use_natural_foreign_keys=False, object_id_field="pk"): | ||
| def register(model): | ||
| # Prevent multiple registration. | ||
| if is_registered(model): | ||
|
|
@@ -401,6 +411,7 @@ def register(model): | |
| for_concrete_model=for_concrete_model, | ||
| ignore_duplicates=ignore_duplicates, | ||
| use_natural_foreign_keys=use_natural_foreign_keys, | ||
| object_id_field=object_id_field, | ||
| ) | ||
| # Register the model. | ||
| _registered_models[_get_registration_key(model)] = version_options | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| # Generated by Django 6.0.4 on 2026-05-02 12:08 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('test_app', '0002_alter_testmodel_related_and_more'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.CreateModel( | ||
| name='TestModelCustomObjectId', | ||
| fields=[ | ||
| ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
| ('slug', models.CharField(max_length=191, unique=True)), | ||
| ('name', models.CharField(default='v1', max_length=191)), | ||
| ], | ||
| ), | ||
| ] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This feels worth optimizing for the case of
object_id_fieldbeing the primary key. It'll save one query for 99.9% of django-reversion users.So a check like
._meta.pk.name == object_id_fieldThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
great yes thanks for catching this