Refs #29898 -- Moved django.db.migrations.operations.utils to django.db.migrations.utils.
This commit is contained in:
parent
e85d9c02ad
commit
d718d99017
|
@ -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):
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue