mysteriendrama/lib/python3.11/site-packages/django/contrib/contenttypes/forms.py
2023-07-22 12:13:39 +02:00

134 lines
3.9 KiB
Python

from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.forms import ModelForm, modelformset_factory
from django.forms.models import BaseModelFormSet
class BaseGenericInlineFormSet(BaseModelFormSet):
"""
A formset for generic inline objects to a parent.
"""
def __init__(
self,
data=None,
files=None,
instance=None,
save_as_new=False,
prefix=None,
queryset=None,
**kwargs,
):
opts = self.model._meta
self.instance = instance
self.rel_name = (
opts.app_label
+ "-"
+ opts.model_name
+ "-"
+ self.ct_field.name
+ "-"
+ self.ct_fk_field.name
)
self.save_as_new = save_as_new
if self.instance is None or self.instance.pk is None:
qs = self.model._default_manager.none()
else:
if queryset is None:
queryset = self.model._default_manager
qs = queryset.filter(
**{
self.ct_field.name: ContentType.objects.get_for_model(
self.instance, for_concrete_model=self.for_concrete_model
),
self.ct_fk_field.name: self.instance.pk,
}
)
super().__init__(queryset=qs, data=data, files=files, prefix=prefix, **kwargs)
def initial_form_count(self):
if self.save_as_new:
return 0
return super().initial_form_count()
@classmethod
def get_default_prefix(cls):
opts = cls.model._meta
return (
opts.app_label
+ "-"
+ opts.model_name
+ "-"
+ cls.ct_field.name
+ "-"
+ cls.ct_fk_field.name
)
def save_new(self, form, commit=True):
setattr(
form.instance,
self.ct_field.get_attname(),
ContentType.objects.get_for_model(self.instance).pk,
)
setattr(form.instance, self.ct_fk_field.get_attname(), self.instance.pk)
return form.save(commit=commit)
def generic_inlineformset_factory(
model,
form=ModelForm,
formset=BaseGenericInlineFormSet,
ct_field="content_type",
fk_field="object_id",
fields=None,
exclude=None,
extra=3,
can_order=False,
can_delete=True,
max_num=None,
formfield_callback=None,
validate_max=False,
for_concrete_model=True,
min_num=None,
validate_min=False,
absolute_max=None,
can_delete_extra=True,
):
"""
Return a ``GenericInlineFormSet`` for the given kwargs.
You must provide ``ct_field`` and ``fk_field`` if they are different from
the defaults ``content_type`` and ``object_id`` respectively.
"""
opts = model._meta
# if there is no field called `ct_field` let the exception propagate
ct_field = opts.get_field(ct_field)
if (
not isinstance(ct_field, models.ForeignKey)
or ct_field.remote_field.model != ContentType
):
raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % ct_field)
fk_field = opts.get_field(fk_field) # let the exception propagate
exclude = [*(exclude or []), ct_field.name, fk_field.name]
FormSet = modelformset_factory(
model,
form=form,
formfield_callback=formfield_callback,
formset=formset,
extra=extra,
can_delete=can_delete,
can_order=can_order,
fields=fields,
exclude=exclude,
max_num=max_num,
validate_max=validate_max,
min_num=min_num,
validate_min=validate_min,
absolute_max=absolute_max,
can_delete_extra=can_delete_extra,
)
FormSet.ct_field = ct_field
FormSet.ct_fk_field = fk_field
FormSet.for_concrete_model = for_concrete_model
return FormSet