Skip to content

Commit

Permalink
Merge pull request #1513 from gtech-mulearn/dev
Browse files Browse the repository at this point in the history
[FEAT] Do iteration #2 org to org link
  • Loading branch information
MZaFaRM authored Nov 3, 2023
2 parents 4f26697 + b334d15 commit 158a4ff
Show file tree
Hide file tree
Showing 2 changed files with 110 additions and 37 deletions.
21 changes: 19 additions & 2 deletions api/dashboard/organisation/organisation_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,23 @@ def get(self, request, org_code):
class OrganizationMergerView(APIView):
permission_classes = [CustomizePermission]

@role_required([RoleType.ADMIN.value])
def get(self, request, organisation_id):
try:
destination = Organization.objects.get(pk=organisation_id)
serializer = OrganizationMergerSerializer(destination, data=request.data)
if not serializer.is_valid():
return CustomResponse(
general_message=serializer.errors
).get_failure_response()

return CustomResponse(response=serializer.data).get_success_response()

except Organization.DoesNotExist:
return CustomResponse(
general_message="An organization with the given id doesn't exist"
).get_failure_response()

@role_required([RoleType.ADMIN.value])
def patch(self, request, organisation_id):
try:
Expand All @@ -449,7 +466,7 @@ def patch(self, request, organisation_id):
general_message=f"Organizations merged successfully into {destination.title}."
).get_success_response()

except Organization.DoesNotExist as e:
except Organization.DoesNotExist:
return CustomResponse(
general_message="Organisation id that was given to merge into does not exist"
general_message="An organization with the given id doesn't exist"
).get_failure_response()
126 changes: 91 additions & 35 deletions api/dashboard/organisation/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,62 +246,118 @@ class Meta:


class OrganizationMergerSerializer(serializers.Serializer):
remove_code = serializers.SlugRelatedField(
slug_field="code", queryset=Organization.objects.all()
update_summary = serializers.SerializerMethodField()
source_org = serializers.SlugRelatedField(
slug_field="code",
queryset=Organization.objects.all(),
write_only=True,
error_messages={
"does_not_exist": "An organization with the given code doesn't exist",
},
)

def validate(self, attrs):
if self.instance.code == attrs.get("remove_code"):
def validate_source_code(self, attrs):
if self.instance.code == attrs.code:
raise serializers.ValidationError(
"Keep code and remove code should not be the same."
"You can't merge an organization into itself."
)
return super().validate(attrs)

def get_update_summary(self, instance):
update_summary = []

# Simulate the transaction
for relation in Organization._meta.related_objects:
if isinstance(
relation,
(models.ForeignKey, models.OneToOneField, models.ManyToManyField),
):
related_model = relation.related_model
related_field_name = relation.field.name
elif isinstance(relation, (models.ManyToOneRel, models.ManyToManyRel)):
related_model = relation.related_model
related_field_name = next(
(
field.name
for field in related_model._meta.fields
if isinstance(field, models.ForeignKey)
and field.related_model == Organization
),
None,
)
else:
continue

if not related_field_name:
continue

if existing_relations := related_model.objects.filter(
**{related_field_name: instance}
):
update_summary.append(
{
"model": related_model._meta.model_name,
"field_name": related_field_name,
"count": existing_relations.count(),
"action": (
"delete"
if isinstance(
relation, (models.OneToOneField, models.OneToOneRel)
)
else "update"
),
"instance_ids": list(
existing_relations.values_list("pk", flat=True)
),
}
)

return update_summary

def update(self, instance, validated_data):
with transaction.atomic():
remove_org = validated_data["remove_code"]
source_org = validated_data["source_org"]

# Fetch and iterate over all relations to the Organization model
for relation in Organization._meta.related_objects:
# We're interested in ForeignKey relations only
# Determine related model and related field name
if isinstance(
relation,
(models.ForeignKey, models.OneToOneField, models.ManyToManyField),
):
related_model = relation.related_model
related_field_name = relation.field.name
elif isinstance(
relation,
(models.ManyToOneRel, models.ManyToManyRel, models.OneToOneRel),
):
elif isinstance(relation, (models.ManyToOneRel, models.ManyToManyRel)):
related_model = relation.related_model
related_field_name = None
for field in related_model._meta.fields:
if (
isinstance(field, models.ForeignKey)
# Find the related field in the related model that links back to Organization
related_field_name = next(
(
field.name
for field in related_model._meta.fields
if isinstance(field, models.ForeignKey)
and field.related_model == Organization
):
related_field_name = field.name
break

if (
not related_field_name
): # If the related field is not found, skip
continue
else:
continue # Skip other types of relations

# Update the ForeignKey in the related model
filter_kwargs = {related_field_name: remove_org}
),
None,
)

# Skip if we couldn't determine the related field
if not related_field_name:
continue

# Prepare filter and update arguments
filter_kwargs = {related_field_name: source_org}
update_kwargs = {related_field_name: instance}
relation_instance = related_model.objects.filter(**filter_kwargs)

# Handle OneToOne relationships: delete existing relation if it points to the instance
if isinstance(relation, (models.OneToOneField, models.OneToOneRel)):
if existing_college := related_model.objects.filter(
if existing_relation := related_model.objects.filter(
**{related_field_name: instance}
):
existing_college.delete()
).first():
existing_relation.delete()

# Update the relations in the related model
related_model.objects.filter(**filter_kwargs).update(**update_kwargs)

relation_instance.update(**update_kwargs)
remove_org.delete()
# Delete the organization that needs to be removed
source_org.delete()

return instance

0 comments on commit 158a4ff

Please sign in to comment.