• Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: по поводу UUID ошибки, в строке append(f.code) наверное надо записать так append(str(f.code))
    А в js, наверное, вместо if (typeof(elem) == 'number'){ надо if (typeof(elem) != 'object'){
  • Как правильно оформлять списки?

    Вот так получилось.
    values = ['text0', 'text1', 'text2', 'text3'];
    ul.specialization__list
      each val in values
        li.specialization__item
          a.specialization__link= val
  • Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: Если поля с чекбоксами создавать самостоятельно, то ModelForm не нужен, а тут получилось, что поля создаются автоматически, средствами джанги. all_functions можно в __init__ указать, но тогда во вьюхе, при создании формы, надо передавать data=request.POST:
    # если пост запрос
    RequirementForm4(data=request.POST, all_functions=Function.objects.filter(id__lt=7))
    # если гет запрос
    RequirementForm4(all_functions=Function.objects.filter(id__lt=7))
    # если без разницы какой запрос
    RequirementForm4(data=request.POST or None, all_functions=Function.objects.filter(id__lt=7))

    Код второй формы
    class RequirementForm2(forms.Form):
        def __init__(self, *args, **kwargs):
            self.function_field = 'function_{}'
            self.instance = kwargs.pop('instance')
            if self.instance.id:
                kwargs.update(initial={self.function_field.format(f): True
                                       for f in self.instance.function.values_list('id', flat=True)})
            super(RequirementForm2, self).__init__(*args, **kwargs)
            self.functions = Function.objects.all().select_related('task', 'task__group')
            self.tree = {}
            for f in self.functions:
                field_name = self.function_field.format(f.id)
                self.fields[field_name] = forms.BooleanField(
                    label=f.name,
                    widget=forms.CheckboxInput(attrs={'value': f.id}),
                    required=False,
                )
    
                self.tree.setdefault(f.task.group.name, {}).setdefault(
                    f.task.name, []
                ).append(BoundField(self, self.fields[field_name], field_name))
    
        def clean(self):
            cd = super(RequirementForm2, self).clean()
            print '---', cd
            return cd
    
        def as_tree(self):
            return render_to_string('core/tree_form.html', {'tree': self.tree})
    
        def save(self):
            ids = self.get_ids()
            ids_current = self.instance.function.values_list('id', flat=True)
            self.instance.function.remove(*list(set(ids_current) - set(ids)))
            self.instance.function.add(*list(set(ids) - set(ids_current)))
            return self.instance
    
        def get_ids(self):
            print self.cleaned_data
            function_field_clean = self.function_field.format('')
            return [key.replace(function_field_clean, '')
                    for key, value in self.cleaned_data.items()
                    if key.startswith(function_field_clean) and value]
  • Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: Вот такой вариант пришел на ум, с javascript
    forms.py
    class RequirementForm4(forms.ModelForm):
        class Meta:
            model = Program
            fields = 'name', 'function'
            widgets = {
                'function': forms.CheckboxSelectMultiple
            }
    
        def __init__(self, *args, **kwargs):
            all_functions = kwargs.pop('all_functions', None)
            super(RequirementForm4, self).__init__(*args, **kwargs)
            if all_functions:
                self.fields['function'].queryset = all_functions
            self.fields['function'].queryset = self.fields['function'].queryset.select_related(
                'task', 'task__group')
    
        def data_tree(self):
            tree = {}
            for f in self.fields['function'].queryset:
                tree.setdefault(
                    f.task.group.name, {}
                ).setdefault(
                    f.task.name, []
                ).append(f.id)
            date_tree_template = "<div class='data-tree' data-tree='{}' data-field='{}'></div>"
            return mark_safe(date_tree_template.format(json.dumps(tree), 'function'))

    views.py
    def test_view(request):
        form = RequirementForm4(request.POST or None, all_functions=Function.objects.filter(id__lt=7))
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                return redirect(reverse('test_view'))
        context = {
            'form': form,
        }
        return render(request, 'core/test_view.html', context)

    test_view.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <form action="" method="post">
        {% csrf_token %}
        {{ form.errors }}
        {{ form.name }}
        <div class="tree">{{ form.function }}</div>
        {{ form.data_tree }}
        <input type="submit">
    </form>
    
    <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
    <script>
        $(function() {
            var tree = $('.data-tree').data('tree')
            var field = $('.data-tree').data('field')
            render_tree()
    
            function render_tree() {
                if(tree != undefined && field != undefined)
                    $(".tree").replaceWith(make_ul(tree))
            }
    
            function make_ul(lst) {
                var html = $('<ul>');
                for(var name in lst)
                    html.append(make_li(name, lst[name]))
                return html;
            }
    
            function make_li(name, elem) {
                var html = $('<li>');
                if (typeof(elem) == 'object') {
                    html.text(name).append(make_ul(elem));
                }
                if (typeof(elem) == 'number'){
                    html.append($('[name='+field+'][value='+elem+']').parent())
                }
                return html;
            }
        });
    </script>
    </body>
    </html>
  • Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: Думаю, эту форму(вторую) лучше переписать)
    На счет ошибки с all_functions, __init__ формы надо переписать на такое.
    def __init__(self, *args, **kwargs):
            ...
            self.all_functions = kwargs.pop('all_functions')
            super(....

    По поводу инстанса. Сейчас могу предложить быстрое решение(быстрое не всегда верное).
    в forms.py добавить еще одну форму
    class ProgramForm(forms.ModelForm):
        class Meta:
            model = Program
            fields = 'name',

    в форме RequirementForm2 добавить проверку инстанса в __init__.
    if self.instance.id:
                kwargs.update(initial={self.function_field.format(f): True
                                       for f in self.instance.function.values_list('id', flat=True)})

    Во вьюхе создания Program(я опираюсь на модели из какого-то прошлого вопроса, сейчас эта модель, наверное, называется Requirement) написать такое.
    def test_view(request):
        instance = Program()
        form_function = ProgramForm(request.POST or None, instance=instance)
        form = RequirementForm2(request.POST or None, instance=instance)
        if request.method == 'POST':
            if form_function.is_valid() and form.is_valid():
                form_function.save()
                form.save()
                return redirect(reverse('test_view'))
        ...

    если будет страница с редактированием, то instance = Program.objects.get()

    Так же по поводу all_functions - это должен быть QuerySet
  • Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: По поводу первой формы. Там костыль, может по этому не работает. У меня все отработало, но вероятность ошибки есть.
    Вчера не внимательно прочитал комментарий про UUID, думаю, что ни чего не изменится, только нужно в форме вместо id указать code.
    Еще хочу уточнить, на момент отображения страницы, инстанс Requirement уже есть или он как раз только создается?
  • Как правильно создать кастомный виджет для поля?

    NogerbekNurzhan: мне кажется, что виджетами такой структуры как на картинке не добиться. Если использовать ModelForm с Requirement, то будут выводиться только те поля которые есть в связи. По поводу форм, мне больше нравится вторая, хотя они обе какие-то страшненькие)). Вероятно отрисовку формы лучше поручить JavaScript(но я над этим вариантом не думал).
  • Как правильно создать кастомный виджет для поля?

    Всё таки вряд ли тут виджет поможет. Виджет относится к полю, а тут полностью отображение формы. В общем как-то так получилось, не уверен, что это верное решение.
    test_view.html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
    
    <form action="" method="post">
        {% csrf_token %}
        {{ form.to_tree }}
        <input type="submit">
    </form>
    
    </body>
    </html>

    views.py
    # -*- coding: utf-8 -*-
    from __future__ import unicode_literals
    
    from django.shortcuts import render, redirect
    from django.urls import reverse
    
    from core.forms import RequirementForm, RequirementForm2
    # RequirementForm без редиректа не очень себя ведет)))
    from core.models import Program
    
    
    def test_view(request):
        form = RequirementForm(request.POST or None, instance=Program.objects.first())
        if request.method == 'POST':
            if form.is_valid():
                form.save()
                return redirect(reverse('test_view'))
        context = {
            'form': form,
        }
        return render(request, 'core/test_view.html', context)

    forms.py
    from django import forms
    from django.forms import BoundField
    from django.template.loader import render_to_string
    
    from core.models import Function
    
    
    class SplitBoundField(BoundField):
        def build_widget_attrs(self, attrs, widget=None):
            self.initial = widget.attrs.get('value') if widget.attrs.get('value') in self.initial else None
            return super(SplitBoundField, self).build_widget_attrs(attrs, widget)
    
    
    class RequirementForm(forms.Form):
        functions = forms.ModelMultipleChoiceField(
            queryset=Function.objects.all().select_related(
                'task').select_related(
                'task__group').order_by(
                'task__group__id', 'task__id'),
            widget=forms.CheckboxSelectMultiple
        )
    
        def __init__(self, *args, **kwargs):
            self.instance = kwargs.pop('instance')
            self.functions_ids = list(self.instance.function.values_list('id', flat=True))
            kwargs.update(initial={'functions': self.functions_ids})
            super(RequirementForm, self).__init__(*args, **kwargs)
    
        def to_tree(self):
            queryset = self.fields['functions'].queryset
            tree = {}
            choices = list(self.fields['functions'].choices)
            for i, f in enumerate(queryset):
                attrs = {'value': choices[i][0]}
                tree.setdefault(f.task.group.name, {}).setdefault(
                    f.task.name, []
                ).append(SplitBoundField(
                    self,
                    forms.MultipleChoiceField(
                        label=choices[i][1],
                        widget=forms.CheckboxInput(attrs=attrs),
                        required=False,
                    ),
                    'functions')
                )
            return render_to_string('core/tree_form.html', context={'tree': tree})
    
        def save(self):
            ids = self.cleaned_data['functions'].values_list('id', flat=True)
            ids_current = self.functions_ids
            self.instance.function.remove(*list(set(ids_current) - set(ids)))
            self.instance.function.add(*list(set(ids) - set(ids_current)))
            return self.instance
    
    
    class RequirementForm2(forms.Form):
        def __init__(self, *args, **kwargs):
            self.function_field = 'function_{}'
            self.instance = kwargs.pop('instance')
            kwargs.update(initial={self.function_field.format(f): True
                                   for f in self.instance.function.values_list('id', flat=True)})
            super(RequirementForm2, self).__init__(*args, **kwargs)
            self.functions = Function.objects.all().select_related('task').select_related('task__group')
            self.tree = {}
            for f in self.functions:
                field_name = self.function_field.format(f.id)
                self.fields[field_name] = forms.BooleanField(
                    label=f.name,
                    widget=forms.CheckboxInput(attrs={'value': 100}),
                    required=False,
                )
    
                self.tree.setdefault(f.task.group.name, {}).setdefault(
                    f.task.name, []
                ).append(BoundField(self, self.fields[field_name], field_name))
    
        def to_tree(self):
            return render_to_string('core/tree_form.html', {'tree': self.tree})
    
        def save(self):
            ids = self.get_ids()
            ids_current = self.instance.function.values_list('id', flat=True)
            self.instance.function.remove(*list(set(ids_current) - set(ids)))
            self.instance.function.add(*list(set(ids) - set(ids_current)))
            return self.instance
    
        def get_ids(self):
            function_field_clean = self.function_field.format('')
            return [key.replace(function_field_clean, '')
                    for key, value in self.cleaned_data.items()
                    if key.startswith(function_field_clean) and value]

    tree_form.html - шаблон древовидной формы
    <ul>
        {% for name, tasks in tree.items %}
        <li>
            {{ name }}
            <ul>
            {% for name, functions in tasks.items %}
                <li>
                    {{ name }}
                    <ul>
                    {% for field in functions %}
                        <li>
                            {{ field }}{{ field.label }}
                        </li>
                    {% endfor %}
                    </ul>
                </li>
            {% endfor %}
            </ul>
        </li>
        {% endfor %}
    </ul>
  • Django, как отнять проценты от суммы?

    sim3x: вероятно у него сдельная оплата за количество символов.
  • Как создать древовидный список с сheckbox?

    NogerbekNurzhan: только до компьютера добрался. Если честно, я не очень представляю как такое делать). Да и думаю, что вопрос уже решён.
  • Как создать древовидный список с сheckbox?

    NogerbekNurzhan: думаю, что тут все ручками надо делать. Проходить по всем Группам, в каждой группе по Таскам и в каждом таске по Функциям. А раскрывающиеся списки это js.
  • Как создать Multiple Select Box в Джанго?

    NogerbekNurzhan: так не получится, чтобы эти стили и скрипты попали в блоки script и style, так как весь шаблон уже отрисован и выдан в браузер.
  • Как создать Multiple Select Box в Джанго?

    NogerbekNurzhan: по идее js и css получать через ajax это не правильно, по этому вынесение их в основной шаблон это верное решение. Единственное, что смущает это то что форма загружается ajax запросом, а не находится уже в шаблоне. А все данные получаемые от сервера через ajax должны быть в json формате без html тегов.
  • Как создать древовидный список с сheckbox?

    В данном случае mptt вряд ли пригодится.
  • Как создать Multiple Select Box в Джанго?

    Павел Аксенов: код $('#id_company').djangoSelect2({multiple: true}); добавлять в beforeSend не стоит, так как в этот момент формы еще нет, тут только отправляется запрос на получение формы. Лучше этот код добавить в конце success
  • Как создать Multiple Select Box в Джанго?

    NogerbekNurzhan: редактор "съел" тэг). {{ product_form.media.css }} и {{ product_form.media.js }} надо добавить после закрывающего тэга формы.
    </form>
    {{ product_form.media.css }}
    {{ product_form.media.js }}
  • Как создать Multiple Select Box в Джанго?

    NogerbekNurzhan: в шаблоне используются блоки style и script, они что-то изменяют в родительской шаблоне, но родительского шаблона нет. Следовательно эти два блока ни где не участвуют, а в отрендеренную страницу они не смогут добавиться. Нужно из шаблона убрать эти блоки, а .media.css и .media.js добавить после тега
  • Как создать Multiple Select Box в Джанго?

    NogerbekNurzhan: возможно widget надо указать в самом поле, а не в Meta. Мне кажется, что на поля переопределенные в форме Meta не влияет.
  • Как создать Multiple Select Box в Джанго?

    В контексте передается product_form, а в шаблоне указано form.
    Надо заменить {{ form.media.css }} и {{ form.media.js }} на {{ product_form.media.css }} и {{ product_form.media.js }}