2
0

generated.py 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. from django.core import checks
  2. from django.db import connections, router
  3. from django.db.models.sql import Query
  4. from . import NOT_PROVIDED, Field
  5. __all__ = ["GeneratedField"]
  6. class GeneratedField(Field):
  7. generated = True
  8. db_returning = True
  9. _query = None
  10. _resolved_expression = None
  11. output_field = None
  12. def __init__(self, *, expression, db_persist=None, output_field=None, **kwargs):
  13. if kwargs.setdefault("editable", False):
  14. raise ValueError("GeneratedField cannot be editable.")
  15. if not kwargs.setdefault("blank", True):
  16. raise ValueError("GeneratedField must be blank.")
  17. if kwargs.get("default", NOT_PROVIDED) is not NOT_PROVIDED:
  18. raise ValueError("GeneratedField cannot have a default.")
  19. if kwargs.get("db_default", NOT_PROVIDED) is not NOT_PROVIDED:
  20. raise ValueError("GeneratedField cannot have a database default.")
  21. if db_persist not in (True, False):
  22. raise ValueError("GeneratedField.db_persist must be True or False.")
  23. self.expression = expression
  24. self._output_field = output_field
  25. self.db_persist = db_persist
  26. super().__init__(**kwargs)
  27. def contribute_to_class(self, *args, **kwargs):
  28. super().contribute_to_class(*args, **kwargs)
  29. self._query = Query(model=self.model, alias_cols=False)
  30. self._resolved_expression = self.expression.resolve_expression(
  31. self._query, allow_joins=False
  32. )
  33. self.output_field = (
  34. self._output_field
  35. if self._output_field is not None
  36. else self._resolved_expression.output_field
  37. )
  38. # Register lookups from the output_field class.
  39. for lookup_name, lookup in self.output_field.get_class_lookups().items():
  40. self.register_lookup(lookup, lookup_name=lookup_name)
  41. def generated_sql(self, connection):
  42. return self._resolved_expression.as_sql(
  43. compiler=connection.ops.compiler("SQLCompiler")(
  44. self._query, connection=connection, using=None
  45. ),
  46. connection=connection,
  47. )
  48. def check(self, **kwargs):
  49. databases = kwargs.get("databases") or []
  50. return [
  51. *super().check(**kwargs),
  52. *self._check_supported(databases),
  53. *self._check_persistence(databases),
  54. ]
  55. def _check_supported(self, databases):
  56. errors = []
  57. for db in databases:
  58. if not router.allow_migrate_model(db, self.model):
  59. continue
  60. connection = connections[db]
  61. if (
  62. self.model._meta.required_db_vendor
  63. and self.model._meta.required_db_vendor != connection.vendor
  64. ):
  65. continue
  66. if not (
  67. connection.features.supports_virtual_generated_columns
  68. or "supports_stored_generated_columns"
  69. in self.model._meta.required_db_features
  70. ) and not (
  71. connection.features.supports_stored_generated_columns
  72. or "supports_virtual_generated_columns"
  73. in self.model._meta.required_db_features
  74. ):
  75. errors.append(
  76. checks.Error(
  77. f"{connection.display_name} does not support GeneratedFields.",
  78. obj=self,
  79. id="fields.E220",
  80. )
  81. )
  82. return errors
  83. def _check_persistence(self, databases):
  84. errors = []
  85. for db in databases:
  86. if not router.allow_migrate_model(db, self.model):
  87. continue
  88. connection = connections[db]
  89. if (
  90. self.model._meta.required_db_vendor
  91. and self.model._meta.required_db_vendor != connection.vendor
  92. ):
  93. continue
  94. if not self.db_persist and not (
  95. connection.features.supports_virtual_generated_columns
  96. or "supports_virtual_generated_columns"
  97. in self.model._meta.required_db_features
  98. ):
  99. errors.append(
  100. checks.Error(
  101. f"{connection.display_name} does not support non-persisted "
  102. "GeneratedFields.",
  103. obj=self,
  104. id="fields.E221",
  105. hint="Set db_persist=True on the field.",
  106. )
  107. )
  108. if self.db_persist and not (
  109. connection.features.supports_stored_generated_columns
  110. or "supports_stored_generated_columns"
  111. in self.model._meta.required_db_features
  112. ):
  113. errors.append(
  114. checks.Error(
  115. f"{connection.display_name} does not support persisted "
  116. "GeneratedFields.",
  117. obj=self,
  118. id="fields.E222",
  119. hint="Set db_persist=False on the field.",
  120. )
  121. )
  122. return errors
  123. def deconstruct(self):
  124. name, path, args, kwargs = super().deconstruct()
  125. del kwargs["blank"]
  126. del kwargs["editable"]
  127. kwargs["db_persist"] = self.db_persist
  128. kwargs["expression"] = self.expression
  129. if self._output_field is not None:
  130. kwargs["output_field"] = self._output_field
  131. return name, path, args, kwargs
  132. def get_internal_type(self):
  133. return self.output_field.get_internal_type()
  134. def db_parameters(self, connection):
  135. return self.output_field.db_parameters(connection)