Refs #29898 -- Moved django.db.migrations.operations.utils to django.db.migrations.utils.

This commit is contained in:
manav014 2021-06-22 09:14:27 +02:00 committed by Mariusz Felisiak
parent e85d9c02ad
commit d718d99017
4 changed files with 78 additions and 77 deletions

View File

@ -1,9 +1,11 @@
from django.core.exceptions import FieldDoesNotExist from django.core.exceptions import FieldDoesNotExist
from django.db.migrations.utils import (
field_is_referenced, field_references, get_references,
)
from django.db.models import NOT_PROVIDED from django.db.models import NOT_PROVIDED
from django.utils.functional import cached_property from django.utils.functional import cached_property
from .base import Operation from .base import Operation
from .utils import field_is_referenced, field_references, get_references
class FieldOperation(Operation): class FieldOperation(Operation):

View File

@ -1,14 +1,15 @@
from django.db import models from django.db import models
from django.db.migrations.operations.base import Operation from django.db.migrations.operations.base import Operation
from django.db.migrations.state import ModelState from django.db.migrations.state import ModelState
from django.db.migrations.utils import resolve_relation from django.db.migrations.utils import (
field_references, get_references, resolve_relation,
)
from django.db.models.options import normalize_together from django.db.models.options import normalize_together
from django.utils.functional import cached_property from django.utils.functional import cached_property
from .fields import ( from .fields import (
AddField, AlterField, FieldOperation, RemoveField, RenameField, AddField, AlterField, FieldOperation, RemoveField, RenameField,
) )
from .utils import field_references, get_references
def _check_for_duplicates(arg_name, objs): def _check_for_duplicates(arg_name, objs):

View File

@ -1,74 +0,0 @@
from collections import namedtuple
from django.db.migrations.utils import resolve_relation
FieldReference = namedtuple('FieldReference', 'to through')
def field_references(
model_tuple,
field,
reference_model_tuple,
reference_field_name=None,
reference_field=None,
):
"""
Return either False or a FieldReference if `field` references provided
context.
False positives can be returned if `reference_field_name` is provided
without `reference_field` because of the introspection limitation it
incurs. This should not be an issue when this function is used to determine
whether or not an optimization can take place.
"""
remote_field = field.remote_field
if not remote_field:
return False
references_to = None
references_through = None
if resolve_relation(remote_field.model, *model_tuple) == reference_model_tuple:
to_fields = getattr(field, 'to_fields', None)
if (
reference_field_name is None or
# Unspecified to_field(s).
to_fields is None or
# Reference to primary key.
(None in to_fields and (reference_field is None or reference_field.primary_key)) or
# Reference to field.
reference_field_name in to_fields
):
references_to = (remote_field, to_fields)
through = getattr(remote_field, 'through', None)
if through and resolve_relation(through, *model_tuple) == reference_model_tuple:
through_fields = remote_field.through_fields
if (
reference_field_name is None or
# Unspecified through_fields.
through_fields is None or
# Reference to field.
reference_field_name in through_fields
):
references_through = (remote_field, through_fields)
if not (references_to or references_through):
return False
return FieldReference(references_to, references_through)
def get_references(state, model_tuple, field_tuple=()):
"""
Generator of (model_state, name, field, reference) referencing
provided context.
If field_tuple is provided only references to this particular field of
model_tuple will be generated.
"""
for state_model_tuple, model_state in state.models.items():
for name, field in model_state.fields.items():
reference = field_references(state_model_tuple, field, model_tuple, *field_tuple)
if reference:
yield model_state, name, field, reference
def field_is_referenced(state, model_tuple, field_tuple):
"""Return whether `field_tuple` is referenced by any state models."""
return next(get_references(state, model_tuple, field_tuple), None) is not None

View File

@ -1,8 +1,11 @@
import datetime import datetime
import re import re
from collections import namedtuple
from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
FieldReference = namedtuple('FieldReference', 'to through')
COMPILED_REGEX_TYPE = type(re.compile('')) COMPILED_REGEX_TYPE = type(re.compile(''))
@ -44,3 +47,72 @@ def resolve_relation(model, app_label=None, model_name=None):
) )
return app_label, model.lower() return app_label, model.lower()
return model._meta.app_label, model._meta.model_name return model._meta.app_label, model._meta.model_name
def field_references(
model_tuple,
field,
reference_model_tuple,
reference_field_name=None,
reference_field=None,
):
"""
Return either False or a FieldReference if `field` references provided
context.
False positives can be returned if `reference_field_name` is provided
without `reference_field` because of the introspection limitation it
incurs. This should not be an issue when this function is used to determine
whether or not an optimization can take place.
"""
remote_field = field.remote_field
if not remote_field:
return False
references_to = None
references_through = None
if resolve_relation(remote_field.model, *model_tuple) == reference_model_tuple:
to_fields = getattr(field, 'to_fields', None)
if (
reference_field_name is None or
# Unspecified to_field(s).
to_fields is None or
# Reference to primary key.
(None in to_fields and (reference_field is None or reference_field.primary_key)) or
# Reference to field.
reference_field_name in to_fields
):
references_to = (remote_field, to_fields)
through = getattr(remote_field, 'through', None)
if through and resolve_relation(through, *model_tuple) == reference_model_tuple:
through_fields = remote_field.through_fields
if (
reference_field_name is None or
# Unspecified through_fields.
through_fields is None or
# Reference to field.
reference_field_name in through_fields
):
references_through = (remote_field, through_fields)
if not (references_to or references_through):
return False
return FieldReference(references_to, references_through)
def get_references(state, model_tuple, field_tuple=()):
"""
Generator of (model_state, name, field, reference) referencing
provided context.
If field_tuple is provided only references to this particular field of
model_tuple will be generated.
"""
for state_model_tuple, model_state in state.models.items():
for name, field in model_state.fields.items():
reference = field_references(state_model_tuple, field, model_tuple, *field_tuple)
if reference:
yield model_state, name, field, reference
def field_is_referenced(state, model_tuple, field_tuple):
"""Return whether `field_tuple` is referenced by any state models."""
return next(get_references(state, model_tuple, field_tuple), None) is not None