-
Notifications
You must be signed in to change notification settings - Fork 173
Description
Problem
Let's assume, we have two models.
models.py:
class Survey(models.Model):
uid = models.UUIDField(verbose_name='UID', unique=True, default=uuid7, editable=False)
title = models.CharField(_('title'), max_length=255)
description = models.TextField('description')
is_active = models.BooleanField(
'active',
default=True,
help_text='Designates whether this object should be treated as active. Unselect this instead of deleting objects.',
)
...
class Feature(models.Model):
uid = models.UUIDField(verbose_name='UID', unique=True, default=uuid7, editable=False)
survey = models.ForeignKey(
Survey,
on_delete=models.CASCADE,
verbose_name='survey',
related_name='features',
related_query_name='feature',
)
display_name = models.CharField('display name', max_length=255)
is_active = models.BooleanField(
'active',
default=True,
help_text='Designates whether this object should be treated as active. Unselect this instead of deleting objects.',
)
....
Now we would like to use UpdateWithInlinesView.
Let's view our form html:
...
<input type="hidden" name="features-0-id" value="7" id="id_features-0-id">
...
<input type="hidden" name="features-1-id" value="8" id="id_features-1-id">
...
<input type="hidden" name="features-2-id" value="9" id="id_features-2-id">
We have Feature instance ids there for each of formset members. It's acceptable for most of projects but we may want to use slug fields (uid in this case).
We have motivation to do it if we use integer PK (for performance reasons) and would like to prevent marketing tracking from our competitors (they may create new features every month and compare feature ids to each other to answer questions like how much new surveys do we have in this month and so on).
form html:
...
<input type="hidden" name="features-0-id" value="0183a330-a8d5-77c2-a3d7-166d6f1c1fe7" id="id_features-0-id">
...
Solution
We replace slugs (uids) to ids here during processing POST data.
First step: we create uids list.
Second step: we load features list of dicts with id and uid data from database based on uids values.
Third step: we replace slugs to id if we know how to do it (we read features).
class FeatureUpdateInline(InlineFormSetFactory): # type: ignore
...
form_class = FeatureInlineForm
formset_class = BaseFeatureFormSet
model = Feature
fields = ('display_name', )
...
@staticmethod
def process_post_data(obj, post_data):
object_id = str(obj.id)
changes = {}
uids = []
# 1
for field_name, value in post_data.items():
if field_name.endswith('-id'):
uids.append(value)
# 2
features = obj.features.filter(is_active=True, uid__in=uids).values('id', 'uid')
# 3
for field_name, value in post_data.items():
if field_name.endswith('-id'):
for feature in features:
if value == str(feature['uid']):
changes[field_name] = str(feature['id'])
return changes
def get_formset_kwargs(self):
kwargs = super().get_formset_kwargs()
if self.request.method in ("POST", "PUT"):
kwargs['data'].update(self.process_post_data(obj=self.object, post_data=kwargs['data']))
print(kwargs['data'].dict())
return kwargs
class SurveyUpdateView(
...
NamedFormsetsMixin,
UpdateWithInlinesView,
):
...
form_class = SurveyUpdateForm
inlines = [FeatureUpdateInline]
inlines_names = ['Features']
...
Suggestions
- Do you have suggestions how to refactor this code?
- Can we somehow add this feature to
django-extra-views?