From dc1a9d5c4f5af189a442edb1808ebf772131167c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Kucharczyk?= Date: Thu, 30 May 2024 11:15:52 +0200 Subject: [PATCH] Make sure attribute chains are evaluated safely --- common/utils.py | 20 ++++++++++++++++++++ games/forms.py | 5 +++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/common/utils.py b/common/utils.py index 422a018..f837ec0 100644 --- a/common/utils.py +++ b/common/utils.py @@ -7,3 +7,23 @@ def safe_division(numerator: int | float, denominator: int | float) -> int | flo return numerator / denominator except ZeroDivisionError: return 0 + +def safe_getattr(obj, attr_chain, default=None): + """ + Safely get the nested attribute from an object. + + Parameters: + obj (object): The object from which to retrieve the attribute. + attr_chain (str): The chain of attributes, separated by dots. + default: The default value to return if any attribute in the chain does not exist. + + Returns: + The value of the nested attribute if it exists, otherwise the default value. + """ + attrs = attr_chain.split('.') + for attr in attrs: + try: + obj = getattr(obj, attr) + except AttributeError: + return default + return obj diff --git a/games/forms.py b/games/forms.py index 29f1009..6ad8b19 100644 --- a/games/forms.py +++ b/games/forms.py @@ -1,5 +1,6 @@ from django import forms from django.urls import reverse +from common.utils import safe_getattr from games.models import Device, Edition, Game, Platform, Purchase, Session @@ -45,8 +46,8 @@ class EditionChoiceField(forms.ModelChoiceField): class IncludePlatformSelect(forms.Select): def create_option(self, name, value, *args, **kwargs): option = super().create_option(name, value, *args, **kwargs) - if value: - option["attrs"]["data-platform"] = value.instance.platform.id + if platform_id := safe_getattr(value, 'instance.platform.id'): + option["attrs"]["data-platform"] = platform_id return option