def test_layout_get_field_names(): layout_1 = Layout( Div('field_name'), 'password' ) assert layout_1.get_field_names() == [ [[0, 0], 'field_name'], [[1], 'password'], ] layout_2 = Layout( Div('field_name'), 'password', Fieldset('legend', 'extra_field') ) assert layout_2.get_field_names() == [ [[0, 0], 'field_name'], [[1], 'password'], [[2, 0], 'extra_field'], ] layout_3 = Layout( Div( Div( Div('email') ), Div('password1'), 'password2' ) ) assert layout_3.get_field_names() == [ [[0, 0, 0, 0], 'email'], [[0, 1, 0], 'password1'], [[0, 2], 'password2'], ]
def test_layout_get_field_names(self): layout_1 = Layout(Div("field_name"), "password") self.assertEqual(layout_1.get_field_names(), [[[0, 0], "field_name"], [[1], "password"]]) layout_2 = Layout(Div("field_name"), "password", Fieldset("legend", "extra_field")) self.assertEqual( layout_2.get_field_names(), [[[0, 0], "field_name"], [[1], "password"], [[2, 0], "extra_field"]] ) layout_3 = Layout(Div(Div(Div("email")), Div("password1"), "password2")) self.assertEqual( layout_3.get_field_names(), [[[0, 0, 0, 0], "email"], [[0, 1, 0], "password1"], [[0, 2], "password2"]] )
def test_layout_get_field_names(): layout_1 = Layout(Div("field_name"), "password") assert layout_1.get_field_names() == [ [[0, 0], "field_name"], [[1], "password"], ] layout_2 = Layout(Div("field_name"), "password", Fieldset("legend", "extra_field")) assert layout_2.get_field_names() == [ [[0, 0], "field_name"], [[1], "password"], [[2, 0], "extra_field"], ] layout_3 = Layout(Div(Div(Div("email")), Div("password1"), "password2")) assert layout_3.get_field_names() == [ [[0, 0, 0, 0], "email"], [[0, 1, 0], "password1"], [[0, 2], "password2"], ]
def remove_excluded_field_from_layout(layout: Layout, excluded_fields: Iterable[str]): """Remove excluded fields, and their parents elements, from the layout The algorithm is very naïve but works: - Get all layout objects in the layout, and sort them lexicographically according to the layout object path inside the layout (the path is the sequence of array index to get the layout object from the layout - for each layout object, determine which fields it contain, by iterating from all fields and keeping the fields whose path includes as a prefix the path of the layout object. If these fields are all in the `excluded_fields` list, we can safely remove it from the layout The sorting in the first step is there to make sure we're removing children before parents, and bigger indexes before smaller indexes, so that we don't have to check that a layout object's parent has not been removed, or compute an offset for the indexes that account for the objects we already deleted. In step 2, we have a special case where if the list of fields included in the layout object is empty, we do not delete it: this case include html elements with warning text, for example, and without this special case, we would delete it every time. """ layout_objects = sorted( layout.get_layout_objects(LayoutObject, greedy=True), key=itemgetter(0), reverse=True, ) fields = layout.get_field_names() excluded_fields = set(excluded_fields) & {f[1] for f in fields} if not excluded_fields: return for pos, _ in layout_objects: relevant_fields = set(f[1] for f in fields if f[0][:len(pos)] == pos) if relevant_fields and excluded_fields.issuperset(relevant_fields): last, *prefix = reversed(pos) obj = layout while prefix: obj = obj[prefix.pop()] del obj[last]