123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- import datetime
- import re
- from collections import namedtuple
-
- from django.db.models.fields.related import RECURSIVE_RELATIONSHIP_CONSTANT
-
- FieldReference = namedtuple("FieldReference", "to through")
-
- COMPILED_REGEX_TYPE = type(re.compile(""))
-
-
- class RegexObject:
- def __init__(self, obj):
- self.pattern = obj.pattern
- self.flags = obj.flags
-
- def __eq__(self, other):
- if not isinstance(other, RegexObject):
- return NotImplemented
- return self.pattern == other.pattern and self.flags == other.flags
-
-
- def get_migration_name_timestamp():
- return datetime.datetime.now().strftime("%Y%m%d_%H%M")
-
-
- def resolve_relation(model, app_label=None, model_name=None):
- """
- Turn a model class or model reference string and return a model tuple.
-
- app_label and model_name are used to resolve the scope of recursive and
- unscoped model relationship.
- """
- if isinstance(model, str):
- if model == RECURSIVE_RELATIONSHIP_CONSTANT:
- if app_label is None or model_name is None:
- raise TypeError(
- "app_label and model_name must be provided to resolve "
- "recursive relationships."
- )
- return app_label, model_name
- if "." in model:
- app_label, model_name = model.split(".", 1)
- return app_label, model_name.lower()
- if app_label is None:
- raise TypeError(
- "app_label must be provided to resolve unscoped model relationships."
- )
- return app_label, model.lower()
- 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
|