| 827 | |
| 828 | |
| 829 | class GenericInlineFormset(BaseModelFormSet): |
| 830 | """A formset for child objects related to a parent.""" |
| 831 | def __init__(self, data=None, files=None, instance=None, save_as_new=False): |
| 832 | self.save_as_new = save_as_new |
| 833 | self.instance = instance |
| 834 | self.rel_name = '-'.join((self.model._meta.app_label, self.model._meta.object_name.lower(), self.ct.name, self.obj_id.name)) |
| 835 | super(GenericInlineFormset, self).__init__(queryset=self.get_queryset(), data=data, files=files, prefix=self.rel_name) |
| 836 | |
| 837 | def get_queryset(self): |
| 838 | if self.instance is None: |
| 839 | return [] |
| 840 | return self.model._default_manager.filter( **{ |
| 841 | self.ct.name : ContentType.objects.get_for_model(self.instance), |
| 842 | self.obj_id.name : self.instance.pk |
| 843 | }) |
| 844 | |
| 845 | def save_new(self, form, commit=True): |
| 846 | kwargs = { |
| 847 | self.ct.get_attname(): ContentType.objects.get_for_model(self.instance).pk, |
| 848 | self.obj_id.get_attname(): self.instance.pk, |
| 849 | } |
| 850 | new_obj = self.model(**kwargs) |
| 851 | return save_instance(form, new_obj, commit=commit) |
| 852 | |
| 853 | class GenericInlineModelAdmin(InlineModelAdmin): |
| 854 | ct_field_name = None |
| 855 | id_field_name = None |
| 856 | formset = GenericInlineFormset |
| 857 | can_delete = True |
| 858 | can_order = True |
| 859 | |
| 860 | def get_formset( self, request, obj=None ): |
| 861 | if self.declared_fieldsets: |
| 862 | fields = flatten_fieldsets(self.declared_fieldsets) |
| 863 | else: |
| 864 | fields = None |
| 865 | |
| 866 | opts = self.model._meta |
| 867 | ct = opts.get_field(self.ct_field_name) |
| 868 | if not isinstance(ct, models.ForeignKey) or ct.rel.to != ContentType: |
| 869 | raise Exception("fk_name '%s' is not a ForeignKey to ContentType" % (self.ct_field_name)) |
| 870 | obj_id = opts.get_field(self.id_field_name) # let the exception propagate |
| 871 | |
| 872 | FormSet = modelformset_factory(self.model, |
| 873 | form=self.form, |
| 874 | formfield_callback=self.formfield_for_dbfield, |
| 875 | formset=self.formset, |
| 876 | extra=self.extra, |
| 877 | fields=fields, |
| 878 | can_delete=self.can_delete, |
| 879 | can_order=self.can_order, |
| 880 | exclude=[ct.name, obj_id.name] ) |
| 881 | FormSet.ct = ct |
| 882 | FormSet.obj_id = obj_id |
| 883 | return FormSet |
| 884 | |
| 885 | class GenericStackedInline(GenericInlineModelAdmin): |
| 886 | template = 'admin/edit_inline/stacked.html' |
| 887 | |
| 888 | class GenericTabularInline(GenericInlineModelAdmin): |
| 889 | template = 'admin/edit_inline/tabular.html' |