Требуется разъяснение по использованию мастеров форм Django 1.4, особенно по предварительному заполнению и сохранению.

Мы создаем мастер, используя новую функциональность мастера форм Django 1.4.
Документация по этому вопросу очень краткая, и мы не можем найти каких-либо сложных примеров. Мы используем мастер именованных шагов (необходим для поддержки используемого нами представления списка/сетки данных) и серверную часть сеанса. Мастер предназначен для редактирования ролей и связанных прав и обеспечивает функции добавления и редактирования. Мы делаем это, спрашивая пользователя на первом этапе, хочет ли он/она добавить или отредактировать.

Следующий шаг зависит от этого выбора; Если пользователь хочет внести изменения, появится экран поиска, за которым следует listview/datagrid, отображающий Результаты. Затем пользователь может выбрать один из результатов и перейти к экрану сведений, а затем FilteredSelectMultiple, что позволяет ему/ей связать права с этой ролью.

Если пользователь хочет добавить новую роль, экраны поиска и результатов пропускаются, и пользователь переходит непосредственно к экрану сведений, а затем к экрану ссылок.
Все это работает очень хорошо, используя condition_dict< /em> в urls.py, но нас интересует пара вещей об общей функциональности:

Когда выбрана конкретная ранее существовавшая роль, как мы можем заполнить детали и экран ссылок соответствующими данными?

Создаем ли мы экземпляр объекта ролей и каким-то образом передаем его двум формам, если да, то где мы его создаем и нужно ли нам делать это для каждой формы отдельно (что кажется немного чрезмерным)?

При сохранении обычной практикой является создание другого экземпляра объекта роли, добавление в него данных формы и сохранение, или мы можем каким-то образом повторно использовать объект, используемый в формах?

Мы попытались перегрузить get_form_instance для возврата экземпляров ролей, и мы рассмотрели instance_dict в документации, но нам кажется, что это неправильный подход, и примеров не найдено. онлайн, и мы даже не уверены, что они используются для предварительного заполнения данных или даже если мы на правильном пути.

Логически я бы сказал, что на шаге, который выбирает существующую роль, мне нужно заполнить переменные мастера, используя экземпляр выбранного объекта, и они отобразятся в формах. В конце мастера мы обращаем процесс и получаем все данные из переменных мастера, добавляем их во вновь созданный объект ролей и сохраняем его. В идеале этот экземпляр сам определяет, нужно ли ему выполнять INSERT или UPDATE, в зависимости от того, заполнен ли первичный ключ.

Если кто-то может привести пример или подтолкнуть в правильном направлении, это было бы очень признательно.

Код класса wizardview в views.py приведен ниже:

class RolesWizard(NamedUrlSessionWizardView):

def get_template_names(self):
    # get template for each step...
    if self.steps.current == 'choice':
        return 'clubassistant/wizard_neworeditrole.html'
    if self.steps.current == 'search':
        return 'clubassistant/wizard_searchrole.html'
    if self.steps.current == 'results':
        return 'clubassistant/wizard_pickrole.html'
    if self.steps.current == 'details':
        return 'clubassistant/wizard_detailsrole.html'
    elif self.steps.current == 'rights':
        return 'clubassistant/wizard_roles.html'

def get_context_data(self, form, **kwargs):
    # get context data to be passed to the respective templates
    context = super(RolesWizard, self).get_context_data(form=form, **kwargs)

    # add the listview in the results screen
    if self.steps.current == 'results':
        # get search text from previous step
        cleaned_data = self.get_cleaned_data_for_step('search')
        table = RolesTable(Roles.objects.filter(
            role_name__contains=cleaned_data['searchrole'])
        )
        RequestConfig(self.request, paginate={
            "per_page": 4,
            }).configure(table)
        # add the listview with results
        context.update({'table': table})

    # add a role instance based on the chosen primary key
    if self.steps.current == 'rights':
        cleaned_data = self.get_cleaned_data_for_step('results')
        role_id = cleaned_data['role_uuid']
        role = get_object_or_404(Roles, pk=role_id)
        context.update({'role': role})

    return context

def done(self, form_list, **kwargs):
    # this code is executed when the wizard needs to be completed

    # combine all forms into a single dictionary
    wizard = self.get_all_cleaned_data()

    if wizard.get("neworeditrole")=="add":
        role = Roles()
    else:
        role = get_object_or_404(Roles, pk=wizard.get("role_uuid"))

    # many-to-many rights/roles
    role.role_rights_new_style.clear()
    for each_right in wizard.get('role_rights_new_style'):
        RightsRoles.objects.create(role=role, right=each_right,)

    # other properties
    for field, value in self.get_cleaned_data_for_step('details'):
        setattr(role, field, value)

    role.save()

    # return to first page of wizard...
    return HttpResponseRedirect('/login/maintenance/roles/wizard/choice/')

person Erik Oosterwaal    schedule 05.04.2012    source источник


Ответы (2)


Для будущих пользователей Google:

У меня был некоторый успех с использованием get_form(), потому что он вызывается перед визуализацией формы. Начните с пары ModelForms:

class Wizard1(models.ModelForm): 
    class Meta:
        model = MyModel
        fields = ('field0', 'model0')
class Wizard2(models.ModelForm): 
    class Meta:
        model = MyModel
        excludes = ('field0', 'model0')

Затем в вашем SessionWizardView:

class MyWizard(SessionWizardView):
    def get_form(self, step=None, data=None, files=None):
        form = super(ExtensionCreationWizard, self).get_form(step, data, files)

        if step is not None and data is not None:
            # get_form is called for validation by get_cleaned_data_for_step()
            return form

        if step == "0":
            # you can set initial values or tweak fields here

        elif step == "1":
            data = self.get_cleaned_data_for_step('0')
            if data is not None:
                form.fields['field1'].initial = data.get('field0')
                form.fields['field2'].widget.attrs['readonly'] = True
                form.fields['field3'].widget.attrs['disabled'] = True
                form.fields['model1'].queryset = Model1.objects.filter(name="foo")

        return form

Все действие выполняется на шаге 1. Вы запрашиваете проверенные данные с шага 0 (который вызывает еще один вызов get_form() для шага 0, поэтому будьте осторожны), а затем вы можете получить доступ к любым значениям, которые были установлены на шаге 0.

Я привел пару примеров настроек, которые вы можете изменить в полях. Вы можете обновить набор запросов, чтобы ограничить значения в ChoiceField, или повторно отобразить значение, но сделать его доступным только для чтения. Одно предостережение, которое я заметил... readonly не работает с ChoiceField. Вы можете сделать его отключенным, но тогда значение не распространяется при отправке формы.

person Eli Burke    schedule 28.02.2013
comment
хм, метод get_form здесь, на мой взгляд, неудачный, поскольку все, что связано с формой, должно храниться в самом классе формы. Лучше использовать get_form_kwargs для установки флагов, а затем использовать их для настройки полей внутри метода инициализации формы. - person mariodev; 09.06.2013

Посмотрим, смогу ли я помочь. Я сделал мастер форм, который добавляет шаги в зависимости от ответов. На каждом этапе я сохраняю все формы в переменной сеанса, например:

def process_step(self, request, form, step):
  request.session['form_list'] = self.form_list
  request.session['initial'] = self.initial

Затем каждый раз при отображении этого представления я создаю новый мастер форм со всеми предыдущими данными:

def dynamic_wizard(request):
  if not request.session.get('form_list'):
        form = Wizard([Form1])
    else:
        form = Wizard(request.session.get('form_list'), initial = request.session['initial'])
    return form(context=RequestContext(request), request=request)
person daigorocub    schedule 21.06.2012