[Python] Предварительно заполнить встроенный FormSet?



Answers

Я потратил немало времени, пытаясь придумать решение, которое я мог бы повторно использовать на разных сайтах. Сообщение Джеймса содержало ключевую часть мудрости расширения BaseInlineFormSet но стратегически BaseFormSet вызовы с BaseFormSet .

AdminInline решение разбито на две части: AdminInline и BaseInlineFormSet .

  1. InlineAdmin динамически генерирует начальное значение, основанное на объекте открытого запроса.
  2. Он использует currying, чтобы выставить начальные значения в пользовательский BaseInlineFormSet через аргументы ключевого слова, переданные конструктору.
  3. Конструктор BaseInlineFormSet начальные значения из списка аргументов ключевых слов и конструкций в обычном режиме.
  4. Последняя часть переопределяет процесс построения формы, изменяя максимальное общее количество форм и используя методы BaseFormSet._construct_form и BaseFormSet._construct_forms

Вот некоторые конкретные фрагменты, использующие классы OP. Я протестировал это против Django 1.2.3. Я настоятельно рекомендую хранить документацию по formset и admin при разработке.

admin.py

from django.utils.functional import curry
from django.contrib import admin
from example_app.forms import *
from example_app.models import *

class AttendanceInline(admin.TabularInline):
    model           = Attendance
    formset         = AttendanceFormSet
    extra           = 5

    def get_formset(self, request, obj=None, **kwargs):
        """
        Pre-populating formset using GET params
        """
        initial = []
        if request.method == "GET":
            #
            # Populate initial based on request
            #
            initial.append({
                'foo': 'bar',
            })
        formset = super(AttendanceInline, self).get_formset(request, obj, **kwargs)
        formset.__init__ = curry(formset.__init__, initial=initial)
        return formset

forms.py

from django.forms import formsets
from django.forms.models import BaseInlineFormSet

class BaseAttendanceFormSet(BaseInlineFormSet):
    def __init__(self, *args, **kwargs):
        """
        Grabs the curried initial values and stores them into a 'private'
        variable. Note: the use of self.__initial is important, using
        self.initial or self._initial will be erased by a parent class
        """
        self.__initial = kwargs.pop('initial', [])
        super(BaseAttendanceFormSet, self).__init__(*args, **kwargs)

    def total_form_count(self):
        return len(self.__initial) + self.extra

    def _construct_forms(self):
        return formsets.BaseFormSet._construct_forms(self)

    def _construct_form(self, i, **kwargs):
        if self.__initial:
            try:
                kwargs['initial'] = self.__initial[i]
            except IndexError:
                pass
        return formsets.BaseFormSet._construct_form(self, i, **kwargs)

AttendanceFormSet = formsets.formset_factory(AttendanceForm, formset=BaseAttendanceFormSet)
Question

Я работаю над формой для участия в группе. Моя идея состоит в том, чтобы иметь раздел формы для ввода информации о событиях для выступления или репетиции. Вот модель таблицы событий:

class Event(models.Model):
    event_id = models.AutoField(primary_key=True)
    date = models.DateField()
    event_type = models.ForeignKey(EventType)
    description = models.TextField()

Затем я хотел бы иметь встроенный FormSet, который связывает участников группы с событием и записывает, присутствовали ли они, отсутствовали или были извинены:

class Attendance(models.Model):
    attendance_id = models.AutoField(primary_key=True)
    event_id = models.ForeignKey(Event)
    member_id = models.ForeignKey(Member)
    attendance_type = models.ForeignKey(AttendanceType)
    comment = models.TextField(blank=True)

Теперь, что я хотел бы сделать, это предварительно заполнить эту встроенную форму Formset для записей всех текущих членов и по умолчанию их присутствовать (около 60 членов). К сожалению, Django не допускает начальных значений в этом случае.

Какие-либо предложения?




Просто переопределите метод «save_new», он работал для меня в Django 1.5.5:

class ModelAAdminFormset(forms.models.BaseInlineFormSet):
    def save_new(self, form, commit=True):
        result = super(ModelAAdminFormset, self).save_new(form, commit=False)
        # modify "result" here
        if commit:
            result.save()
        return result



Вот как я решил проблему. Есть немного компромисс в создании и удалении записей, но код чист ...

def manage_event(request, event_id):
    """
    Add a boolean field 'record_saved' (default to False) to the Event model
    Edit an existing Event record or, if the record does not exist:
    - create and save a new Event record
    - create and save Attendance records for each Member
    Clean up any unsaved records each time you're using this view
    """
    # delete any "unsaved" Event records (cascading into Attendance records)
    Event.objects.filter(record_saved=False).delete()
    try:
        my_event = Event.objects.get(pk=int(event_id))
    except Event.DoesNotExist:
        # create a new Event record
        my_event = Event.objects.create()
        # create an Attendance object for each Member with the currect Event id
        for m in Members.objects.get.all():
            Attendance.objects.create(event_id=my_event.id, member_id=m.id)
    AttendanceFormSet = inlineformset_factory(Event, Attendance, 
                                        can_delete=False, 
                                        extra=0, 
                                        form=AttendanceForm)
    if request.method == "POST":
        form = EventForm(request.POST, request.FILES, instance=my_event)
        formset = AttendanceFormSet(request.POST, request.FILES, 
                                        instance=my_event)
        if formset.is_valid() and form.is_valid():
            # set record_saved to True before saving
            e = form.save(commit=False)
            e.record_saved=True
            e.save()
            formset.save()
            return HttpResponseRedirect('/')
    else:
        form = EventForm(instance=my_event)
        formset = OptieFormSet(instance=my_event)
    return render_to_response("edit_event.html", {
                            "form":form, 
                            "formset": formset,
                            }, 
                            context_instance=RequestContext(request))



Я столкнулся с той же проблемой.

Вы можете сделать это через JavaScript, сделать простой JS, который делает аякс-вызов для всех участников группы и заполняет форму.

В этом решении отсутствует принцип DRY, потому что вам нужно написать это для каждой встроенной формы.




Я столкнулся с этим вопросом -6 лет спустя, и теперь мы на Django 1.8.

Еще нет абсолютно чистого, короткого ответа на вопрос.

Проблема заключается в файле ModelAdmin._create_formsets () github ; Мое решение состоит в том, чтобы переопределить его и ввести исходные данные, которые я хочу где-то вокруг выделенных строк в ссылке github.

Мне также пришлось переопределить InlineModelAdmin.get_extra (), чтобы «иметь комнату» для исходных данных. Влево по умолчанию отображается только 3 исходных данных

Я считаю, что в будущих версиях должен быть более чистый ответ




Links