Development of an internal social media platform with personalised dashboards for students
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

forms.py 3.5KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. from django.contrib.contenttypes.models import ContentType
  2. from django.db import models
  3. from django.forms import ModelForm, modelformset_factory
  4. from django.forms.models import BaseModelFormSet
  5. class BaseGenericInlineFormSet(BaseModelFormSet):
  6. """
  7. A formset for generic inline objects to a parent.
  8. """
  9. def __init__(self, data=None, files=None, instance=None, save_as_new=False,
  10. prefix=None, queryset=None, **kwargs):
  11. opts = self.model._meta
  12. self.instance = instance
  13. self.rel_name = (
  14. opts.app_label + '-' + opts.model_name + '-' +
  15. self.ct_field.name + '-' + self.ct_fk_field.name
  16. )
  17. self.save_as_new = save_as_new
  18. if self.instance is None or self.instance.pk is None:
  19. qs = self.model._default_manager.none()
  20. else:
  21. if queryset is None:
  22. queryset = self.model._default_manager
  23. qs = queryset.filter(**{
  24. self.ct_field.name: ContentType.objects.get_for_model(
  25. self.instance, for_concrete_model=self.for_concrete_model),
  26. self.ct_fk_field.name: self.instance.pk,
  27. })
  28. super().__init__(queryset=qs, data=data, files=files, prefix=prefix, **kwargs)
  29. def initial_form_count(self):
  30. if self.save_as_new:
  31. return 0
  32. return super().initial_form_count()
  33. @classmethod
  34. def get_default_prefix(cls):
  35. opts = cls.model._meta
  36. return (
  37. opts.app_label + '-' + opts.model_name + '-' +
  38. cls.ct_field.name + '-' + cls.ct_fk_field.name
  39. )
  40. def save_new(self, form, commit=True):
  41. setattr(form.instance, self.ct_field.get_attname(), ContentType.objects.get_for_model(self.instance).pk)
  42. setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk)
  43. return form.save(commit=commit)
  44. def generic_inlineformset_factory(model, form=ModelForm,
  45. formset=BaseGenericInlineFormSet,
  46. ct_field="content_type", fk_field="object_id",
  47. fields=None, exclude=None,
  48. extra=3, can_order=False, can_delete=True,
  49. max_num=None, formfield_callback=None,
  50. validate_max=False, for_concrete_model=True,
  51. min_num=None, validate_min=False):
  52. """
  53. Return a ``GenericInlineFormSet`` for the given kwargs.
  54. You must provide ``ct_field`` and ``fk_field`` if they are different from
  55. the defaults ``content_type`` and ``object_id`` respectively.
  56. """
  57. opts = model._meta
  58. # if there is no field called `ct_field` let the exception propagate
  59. ct_field = opts.get_field(ct_field)
  60. if not isinstance(ct_field, models.ForeignKey) or ct_field.remote_field.model != ContentType:
  61. raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
  62. fk_field = opts.get_field(fk_field) # let the exception propagate
  63. exclude = [*(exclude or []), ct_field.name, fk_field.name]
  64. FormSet = modelformset_factory(
  65. model, form=form, formfield_callback=formfield_callback,
  66. formset=formset, extra=extra, can_delete=can_delete,
  67. can_order=can_order, fields=fields, exclude=exclude, max_num=max_num,
  68. validate_max=validate_max, min_num=min_num, validate_min=validate_min,
  69. )
  70. FormSet.ct_field = ct_field
  71. FormSet.ct_fk_field = fk_field
  72. FormSet.for_concrete_model = for_concrete_model
  73. return FormSet