def __init__(self, model, admin_site, *args, **kwargs): super(PolymorphicParentModelAdmin, self).__init__(model, admin_site, *args, **kwargs) self._is_setup = False if self.base_model is None: self.base_model = get_base_polymorphic_model(model)
def add_inline(self, indexes=None, model=None, **kwargs): model_name = "%s-%s" % (model._meta.app_label, model._meta.model_name) if issubclass(model, PolymorphicModel): base_model = get_base_polymorphic_model(model) else: base_model = model base_model_identifier = "%s-%s" % (base_model._meta.app_label, base_model._meta.model_name) if indexes: item = self.get_item(indexes) ctx_id = item.get_attribute("id") group_el = self.selenium.execute_script( 'return $(arguments[0]).closest(".djn-group")[0]', item) else: group_el = self.get_group([base_model_identifier]) ctx_id = group_el.get_attribute('id') error_desc = "%s in inline %s" % (model, indexes) add_selector = "#%s .djn-add-item a.djn-add-handler.djn-model-%s" % ( ctx_id, base_model_identifier) add_els = self.selenium.find_elements_by_css_selector(add_selector) self.assertNotEqual( len(add_els), 0, "No inline add handlers found for %s" % (error_desc)) self.click(add_els[0]) add_link_selector = "return $('.polymorphic-type-menu:visible [data-type=\"%s\"]')[0]" % ( model_name) poly_add_link = self.selenium.execute_script(add_link_selector) if poly_add_link: poly_add_link.click() indexes = self._normalize_indexes(indexes) group_el = self.selenium.execute_script( 'return $(arguments[0]).closest(".djn-group")[0]', add_els[0]) group_id = group_el.get_attribute('id') items_el = self.selenium.find_element_by_css_selector( '#%(id)s > .djn-fieldset > .djn-items, ' "#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, " '#%(id)s > .djn-items' % {'id': group_id}) num_inlines = len( items_el.find_elements_by_xpath( './*[%s and not(%s)]' % (xpath_item(), xpath_cls('djn-empty-form')))) new_index = num_inlines - 1 indexes.append([model_name, new_index]) for field_name, val in six.iteritems(kwargs): self.set_field(field_name, val, indexes=indexes) return indexes
def test_get_base_polymorphic_model_skip_abstract(self): """ Skipping abstract models that can't be used for querying. """ class A(PolymorphicModel): class Meta: abstract = True class B(A): pass class C(B): pass self.assertIs(get_base_polymorphic_model(A), None) self.assertIs(get_base_polymorphic_model(B), B) self.assertIs(get_base_polymorphic_model(C), B) self.assertIs(get_base_polymorphic_model(C, allow_abstract=True), A)
def add_inline(self, indexes=None, model=None, **kwargs): model_name = "%s-%s" % (model._meta.app_label, model._meta.model_name) if issubclass(model, PolymorphicModel): base_model = get_base_polymorphic_model(model) else: base_model = model base_model_identifier = "%s-%s" % ( base_model._meta.app_label, base_model._meta.model_name) if indexes: item = self.get_item(indexes) group_el = self.selenium.execute_script( 'return $(arguments[0]).closest(".djn-group")[0]', item) else: group_el = self.get_group([base_model_identifier]) group_id = group_el.get_attribute('id') error_desc = "%s in inline %s" % (model, indexes) add_selector = "#%s .djn-add-item a.djn-add-handler.djn-model-%s" % ( group_id, base_model_identifier) add_els = self.selenium.find_elements_by_css_selector(add_selector) self.assertNotEqual(len(add_els), 0, "No inline add handlers found for %s" % (error_desc)) self.click(add_els[0]) add_link_selector = "return $('.polymorphic-type-menu:visible [data-type=\"%s\"]')[0]" % ( model_name) poly_add_link = self.selenium.execute_script(add_link_selector) if poly_add_link: poly_add_link.click() indexes = self._normalize_indexes(indexes) group_el = self.selenium.execute_script( 'return $(arguments[0]).closest(".djn-group")[0]', add_els[0]) group_id = group_el.get_attribute('id') items_el = self.selenium.find_element_by_css_selector( '#%(id)s > .djn-fieldset > .djn-items, ' "#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, " '#%(id)s > .djn-items' % {'id': group_id}) num_inlines = len(items_el.find_elements_by_xpath( './*[%s and not(%s)]' % (xpath_item(), xpath_cls('djn-empty-form')))) new_index = num_inlines - 1 indexes.append([model_name, new_index]) for field_name, val in six.iteritems(kwargs): self.set_field(field_name, val, indexes=indexes) return indexes
def delete_inline(self, indexes): indexes = self._normalize_indexes(indexes) model_id = indexes[-1][0] app_label, model_name = model_id.split('-') model_cls = apps.get_model(app_label, model_name) if issubclass(model_cls, PolymorphicModel): base_model_cls = get_base_polymorphic_model(model_cls) else: base_model_cls = model_cls base_model_id = "%s-%s" % (base_model_cls._meta.app_label, base_model_cls._meta.model_name) item_id = self.get_item(indexes).get_attribute('id') delete_selector = "#%s .djn-delete-handler.djn-model-%s" % ( item_id, base_model_id) with self.clickable_selector(delete_selector) as el: self.click(el) if self.has_grappelli: undelete_selector = "#%s.grp-predelete .grp-delete-handler.djn-model-%s" % ( item_id, base_model_id) self.wait_until_clickable_selector(undelete_selector)
def delete_inline(self, indexes): indexes = self._normalize_indexes(indexes) model_id = indexes[-1][0] app_label, model_name = model_id.split('-') model_cls = apps.get_model(app_label, model_name) if issubclass(model_cls, PolymorphicModel): base_model_cls = get_base_polymorphic_model(model_cls) else: base_model_cls = model_cls base_model_id = "%s-%s" % ( base_model_cls._meta.app_label, base_model_cls._meta.model_name) item_id = self.get_item(indexes).get_attribute('id') delete_selector = "#%s .djn-delete-handler.djn-model-%s" % ( item_id, base_model_id) with self.clickable_selector(delete_selector) as el: self.click(el) if self.has_grappelli: undelete_selector = "#%s.grp-predelete .grp-delete-handler.djn-model-%s" % ( item_id, base_model_id) self.wait_until_clickable_selector(undelete_selector)
def test_get_base_polymorphic_model(self): """ Test that finding the base polymorphic model works. """ # Finds the base from every level (including lowest) self.assertIs(get_base_polymorphic_model(Model2D), Model2A) self.assertIs(get_base_polymorphic_model(Model2C), Model2A) self.assertIs(get_base_polymorphic_model(Model2B), Model2A) self.assertIs(get_base_polymorphic_model(Model2A), Model2A) # Properly handles multiple inheritance self.assertIs(get_base_polymorphic_model(Enhance_Inherit), Enhance_Base) # Ignores PolymorphicModel itself. self.assertIs(get_base_polymorphic_model(PolymorphicModel), None)
def get_item(self, indexes): indexes = self._normalize_indexes(indexes) group_indexes = indexes[:-1] model_id, item_index = indexes[-1] app_label, model_name = model_id.split('-') model_cls = apps.get_model(app_label, model_name) if issubclass(model_cls, PolymorphicModel): base_model_cls = get_base_polymorphic_model(model_cls) else: base_model_cls = model_cls base_model_id = "%s-%s" % (base_model_cls._meta.app_label, base_model_cls._meta.model_name) try: group = self.get_group(indexes=group_indexes + [base_model_id]) except TypeError: group = self.get_group(indexes=group_indexes + [model_id]) group_id = group.get_attribute('id') djn_items = self.selenium.find_element_by_css_selector( "#%(id)s > .djn-fieldset > .djn-items, " "#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, " "#%(id)s > .djn-items" % {'id': group_id}) model_name, item_index = indexes[-1] return djn_items.find_element_by_xpath("./*[%s][%d]" % (xpath_item(), item_index + 1))
def get_item(self, indexes): indexes = self._normalize_indexes(indexes) group_indexes = indexes[:-1] model_id, item_index = indexes[-1] app_label, model_name = model_id.split('-') model_cls = apps.get_model(app_label, model_name) if issubclass(model_cls, PolymorphicModel): base_model_cls = get_base_polymorphic_model(model_cls) else: base_model_cls = model_cls base_model_id = "%s-%s" % ( base_model_cls._meta.app_label, base_model_cls._meta.model_name) try: group = self.get_group(indexes=group_indexes + [base_model_id]) except TypeError: group = self.get_group(indexes=group_indexes + [model_id]) group_id = group.get_attribute('id') djn_items = self.selenium.find_element_by_css_selector( "#%(id)s > .djn-fieldset > .djn-items, " "#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, " "#%(id)s > .djn-items" % {'id': group_id}) model_name, item_index = indexes[-1] return djn_items.find_element_by_xpath( "./*[%s][%d]" % (xpath_item(), item_index + 1))
def __init__(self, model, admin_site, *args, **kwargs): super(PolymorphicChildModelAdmin, self).__init__(model, admin_site, *args, **kwargs) if self.base_model is None: self.base_model = get_base_polymorphic_model(model)
def save_existing_objects(self, initial_forms=None, commit=True): """ Identical to parent class, except ``self.initial_forms`` is replaced with ``initial_forms``, passed as parameter. """ if not initial_forms: return [] saved_instances = [] forms_to_delete = self.deleted_forms for form in initial_forms: pk_name = self._pk_field.name if not hasattr(form, '_raw_value'): # Django 1.9+ raw_pk_value = form.fields[pk_name].widget.value_from_datadict( form.data, form.files, form.add_prefix(pk_name)) else: raw_pk_value = form._raw_value(pk_name) # clean() for different types of PK fields can sometimes return # the model instance, and sometimes the PK. Handle either. if self._should_delete_form(form): pk_value = raw_pk_value else: try: pk_value = form.fields[pk_name].clean(raw_pk_value) except ValidationError: # The current form's instance was initially nested under # a form that was deleted, which causes the pk clean to # fail (because the instance has been deleted). To get # around this we clear the pk and save it as if it were new. with mutable_querydict(form.data): form.data[form.add_prefix(pk_name)] = '' if not form.has_changed(): form.__dict__['changed_data'].append(pk_name) saved_instances.extend( self.save_new_objects([form], commit)) continue pk_value = getattr(pk_value, 'pk', pk_value) obj = None if obj is None and form.instance and pk_value: model_cls = form.instance.__class__ try: obj = model_cls.objects.get(pk=pk_value) except model_cls.DoesNotExist: if pk_value and force_str( form.instance.pk) == force_str(pk_value): obj = form.instance if obj is None: obj = self._existing_object(pk_value) if obj is None or not obj.pk: continue if form in forms_to_delete: self.deleted_objects.append(obj) model_cls = type(obj) base_model_cls = get_base_polymorphic_model(type(obj)) if not base_model_cls: self.delete_existing(obj, commit=commit) else: # Special polymorphic delete handling try: self.delete_existing(obj, commit=commit) except base_model_cls.DoesNotExist: pass continue # fk_val: The value one should find in the form's foreign key field old_ct_val = ct_val = ContentType.objects.get_for_model( self.instance.__class__).pk old_fk_val = fk_val = self.instance.pk if form.instance.pk: original_instance = self.model.objects.get(pk=form.instance.pk) fk_field = getattr(self, 'fk', getattr(self, 'ct_fk_field', None)) if fk_field: old_fk_val = getattr(original_instance, fk_field.get_attname()) ct_field = getattr(self, 'ct_field', None) if ct_field: old_ct_val = getattr(original_instance, ct_field.get_attname()) if form.has_changed( ) or fk_val != old_fk_val or ct_val != old_ct_val: self.changed_objects.append((obj, form.changed_data)) saved_instances.append( self.save_existing(form, obj, commit=commit)) if not commit: self.saved_forms.append(form) return saved_instances