| 90 | |
| 91 | |
| 92 | class Feature( # type: ignore[django-manager-missing] |
| 93 | SoftDeleteExportableModel, |
| 94 | CustomLifecycleModelMixin, |
| 95 | abstract_base_auditable_model_factory(["uuid"]), # type: ignore[misc] |
| 96 | ): |
| 97 | name = models.CharField(max_length=2000) |
| 98 | created_date = models.DateTimeField("DateCreated", auto_now_add=True) |
| 99 | project = models.ForeignKey( |
| 100 | Project, |
| 101 | related_name="features", |
| 102 | help_text=( |
| 103 | "Changing the project selected will remove previous Feature States for the previously " |
| 104 | "associated projects Environments that are related to this Feature. New default " |
| 105 | "Feature States will be created for the new selected projects Environments for this " |
| 106 | "Feature. Also this will remove any Tags associated with a feature as Tags are Project defined" |
| 107 | ), |
| 108 | # Cascade deletes are decouple from the Django ORM. See this PR for details. |
| 109 | # https://github.com/Flagsmith/flagsmith/pull/3360/ |
| 110 | on_delete=models.DO_NOTHING, |
| 111 | ) |
| 112 | initial_value = models.CharField( |
| 113 | max_length=settings.FEATURE_VALUE_LIMIT, null=True, default=None, blank=True |
| 114 | ) |
| 115 | description = models.TextField(null=True, blank=True) |
| 116 | default_enabled = models.BooleanField(default=False) |
| 117 | type = models.CharField( |
| 118 | max_length=50, blank=True, default=STANDARD, choices=FEATURE_TYPE_CHOICES |
| 119 | ) |
| 120 | tags = models.ManyToManyField(Tag, blank=True) |
| 121 | is_archived = models.BooleanField(default=False) |
| 122 | owners = models.ManyToManyField( |
| 123 | "users.FFAdminUser", related_name="owned_features", blank=True |
| 124 | ) |
| 125 | group_owners = models.ManyToManyField( |
| 126 | "users.UserPermissionGroup", related_name="owned_features", blank=True |
| 127 | ) |
| 128 | |
| 129 | is_server_key_only = models.BooleanField(default=False) |
| 130 | |
| 131 | history_record_class_path = "features.models.HistoricalFeature" |
| 132 | related_object_type = RelatedObjectType.FEATURE |
| 133 | |
| 134 | objects = FeatureManager() # type: ignore[misc] |
| 135 | |
| 136 | metadata = GenericRelation(Metadata) |
| 137 | |
| 138 | class Meta: |
| 139 | # Note: uniqueness index is added in explicit SQL in the migrations (See 0005, 0050) |
| 140 | # TODO: after upgrade to Django 4.0 use UniqueConstraint() |
| 141 | ordering = ("id",) # explicit ordering to prevent pagination warnings |
| 142 | |
| 143 | @hook(AFTER_SAVE) # type: ignore[misc] |
| 144 | def create_github_comment(self) -> None: |
| 145 | from integrations.github.github import call_github_task |
| 146 | |
| 147 | if ( |
| 148 | self.external_resources.exists() |
| 149 | and self.project.github_project.exists() |
searching dependent graphs…