Created
January 29, 2016 14:42
-
-
Save jpic/f4cd57d061a05ac34ade to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
ModelForm mixin that supports virtual fields, ie. GenericForeignKey. | |
By default, Django's ModelForm will not update the instance's virtual fields | |
even if a matching form field was given to the form. For example:: | |
class YourForm(forms.ModelForm): | |
gfk_name = SomeField() | |
instance = YourModel.objects.get(pk=4) | |
# Django doesn't populate virtual fields by default, compensate manually: | |
initial = {'gfk_name': instance.gfk_name} | |
# Use the form as usual | |
form = YourForm(request.POST, instance=instance, initial=initial) | |
instance = form.save() | |
# instance.gfk_name will not be set, compensate manually: | |
instance.gfk_ctype = ContentType.objects.get_for_model( | |
instance.cleaned_data['gfk_name']) | |
instance.gfk_object_id = instance.cleaned_data['gfk_name'].pk | |
With YourForm(GenericForeignKeyModelFormMixin, forms.ModelForm) instead of | |
YourForm(forms.ModelForm), GenericForeignKeys are supported automatically. | |
""" | |
from django.contrib.contenttypes.models import ContentType | |
class GenericForeignKeyModelFormMixin(object): | |
""" | |
Enable virtual field (generic foreign key) handling in django's ModelForm. | |
- treat virtual fields like GenericForeignKey as normal fields, | |
- when setting a GenericForeignKey value, also set the object id and | |
content type id fields. | |
Probably, django doesn't do that for legacy reasons: virtual fields were | |
added after ModelForm and simply nobody asked django to add virtual field | |
support in ModelForm. | |
""" | |
def __init__(self, *args, **kwargs): | |
""" | |
The constructor adds virtual field values to | |
:py:attr:`django:django.forms.Form.initial` | |
""" | |
super(GenericForeignKeyModelFormMixin, self).__init__(*args, **kwargs) | |
# do what model_to_dict doesn't | |
for field in self._meta.model._meta.virtual_fields: | |
try: | |
self.initial[field.name] = getattr( | |
self.instance, | |
field.name, | |
None | |
) | |
except: | |
continue | |
def _post_clean(self): | |
""" | |
What ModelForm does, but also set virtual field values from | |
cleaned_data. | |
""" | |
super(GenericForeignKeyModelFormMixin, self)._post_clean() | |
# take care of virtual fields since django doesn't | |
for field in self._meta.model._meta.virtual_fields: | |
value = self.cleaned_data.get(field.name, None) | |
if value: | |
setattr(self.instance, field.name, value) | |
self.cleaned_data[field.ct_field] = \ | |
ContentType.objects.get_for_model(value) | |
self.cleaned_data[field.fk_field] = value.pk |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment